diff --git a/app/assets/stylesheets/components/_btn.scss b/app/assets/stylesheets/components/_btn.scss index 1208f91f830..c0e0873d926 100644 --- a/app/assets/stylesheets/components/_btn.scss +++ b/app/assets/stylesheets/components/_btn.scss @@ -16,7 +16,7 @@ } .usa-button:disabled.usa-button--active, -.usa-button--disabled.usa-button--active { +[aria-disabled='true'].usa-button--active { &:not( .usa-button--unstyled, .usa-button--secondary, @@ -24,8 +24,38 @@ .usa-button--accent-warm, .usa-button--base, .usa-button--outline, - .usa-button--inverse + .usa-button--inverse, + .usa-button--danger ) { @include set-text-and-bg('primary-darker', $context: 'Button'); } } + +// Upstream: https://github.com/18F/identity-design-system/pull/383 +.usa-button--danger.usa-button--outline { + &:not(:disabled, [aria-disabled='true']) { + background-color: color('white'); + box-shadow: inset 0 0 0 $theme-button-stroke-width color('secondary'); + color: color('secondary'); + + &:hover, + &.usa-button--hover { + background-color: color('secondary-lightest'); + box-shadow: inset 0 0 0 $theme-button-stroke-width color('secondary-dark'); + color: color('secondary-dark'); + } + } + + &:active, + &.usa-button--active { + &, + &:focus, + &.usa-button--focus, + &:hover, + &.usa-button--hover { + background-color: color('secondary-lighter'); + box-shadow: inset 0 0 0 $theme-button-stroke-width color('secondary-darker'); + color: color('secondary-darker'); + } + } +} diff --git a/app/components/manageable_authenticator_component.html.erb b/app/components/manageable_authenticator_component.html.erb new file mode 100644 index 00000000000..6b411585a00 --- /dev/null +++ b/app/components/manageable_authenticator_component.html.erb @@ -0,0 +1,120 @@ +<%= content_tag( + :'lg-manageable-authenticator', + **tag_options, + 'api-url': manage_api_url, + 'configuration-name': configuration.name, + 'unique-id': unique_id, + 'reauthenticate-at': reauthenticate_at.iso8601, + 'reauthentication-url': reauthentication_url, + ) do %> + <%= content_tag( + :script, + strings. + slice(:renamed, :delete_confirm, :deleted). + transform_keys { |key| key.to_s.camelcase(:lower) }. + to_json, + { + type: 'application/json', + class: 'manageable-authenticator__strings', + }, + false, + ) %> + <%= tag.div( + class: 'manageable-authenticator__edit', + tabindex: '-1', + role: 'group', + aria: { labelledby: "manageable-authenticator-manage-accessible-label-#{unique_id}" }, + ) do %> + <%= render AlertComponent.new( + type: :success, + class: 'manageable-authenticator__alert', + tabindex: '-1', + ) %> +
+ <%= tag.input( + class: 'manageable-authenticator__rename-input usa-input width-full', + aria: { label: t('components.manageable_authenticator.nickname') }, + value: configuration.name, + ) %> +
+ <%= render SpinnerButtonComponent.new( + type: :submit, + wrapper_options: { class: 'manageable-authenticator__save-rename-button' }, + action_message: t('components.manageable_authenticator.saving'), + long_wait_duration: Float::INFINITY, + ).with_content(t('components.manageable_authenticator.save')) %> + <%= render ButtonComponent.new( + type: :button, + outline: true, + class: 'margin-left-1 manageable-authenticator__cancel-rename-button', + ).with_content(t('components.manageable_authenticator.cancel')) %> +
+
+
+ <%= t('components.manageable_authenticator.nickname') %>: + + <%= configuration.name %> + +
+ <%= t( + 'components.manageable_authenticator.created_on', + date: l(configuration.created_at, format: :event_date), + ) %> +
+
+
+ <%= render ButtonComponent.new( + type: :button, + class: 'manageable-authenticator__rename-button', + ).with_content(t('components.manageable_authenticator.rename')) %> + <%= render SpinnerButtonComponent.new( + type: :button, + danger: true, + outline: true, + wrapper_options: { class: 'manageable-authenticator__delete-button' }, + class: 'margin-left-1', + action_message: t('components.manageable_authenticator.deleting'), + long_wait_duration: Float::INFINITY, + ).with_content(t('components.manageable_authenticator.delete')) %> +
+
+ <%= render ButtonComponent.new( + type: :button, + outline: true, + class: 'manageable-authenticator__done-button', + ).with_content(t('components.manageable_authenticator.done')) %> +
+
+
+ <% end %> +
+
<%= configuration.name %>
+
+ <%= render ButtonComponent.new( + action: ->(**tag_options, &block) { link_to(manage_url, **tag_options, &block) }, + type: :button, + unstyled: true, + class: 'no-js', + ) do %> + + + <%= strings[:manage_accessible_label] %>: <%= tag.span(configuration.name, class: 'manageable-authenticator__name') %> + + <% end %> + <%= render ButtonComponent.new( + type: :button, + unstyled: true, + class: 'js manageable-authenticator__manage-button', + ) do %> + + + <%= strings[:manage_accessible_label] %>: <%= tag.span(configuration.name, class: 'manageable-authenticator__name') %> + + <% end %> +
+
+<% end %> diff --git a/app/components/manageable_authenticator_component.rb b/app/components/manageable_authenticator_component.rb new file mode 100644 index 00000000000..c6c73796844 --- /dev/null +++ b/app/components/manageable_authenticator_component.rb @@ -0,0 +1,57 @@ +class ManageableAuthenticatorComponent < BaseComponent + attr_reader :configuration, + :user_session, + :manage_api_url, + :manage_url, + :custom_strings, + :tag_options + + def initialize( + configuration:, + user_session:, + manage_api_url:, + manage_url:, + custom_strings: {}, + **tag_options + ) + if ![:name, :id, :created_at].all? { |method| configuration.respond_to?(method) } + raise ArgumentError, '`configuration` must respond to `name`, `id`, `created_at`' + end + + @configuration = configuration + @user_session = user_session + @manage_api_url = manage_api_url + @manage_url = manage_url + @custom_strings = custom_strings + @tag_options = tag_options + end + + def reauthentication_url + account_reauthentication_path(manage_authenticator: unique_id) + end + + def unique_id + @unique_id ||= [configuration.class.name.downcase, configuration.id].join('-') + end + + def strings + default_strings.merge(custom_strings) + end + + delegate :reauthenticate_at, to: :auth_methods_session + + private + + def auth_methods_session + @auth_methods_session ||= AuthMethodsSession.new(user_session:) + end + + def default_strings + { + renamed: t('components.manageable_authenticator.renamed'), + delete_confirm: t('components.manageable_authenticator.delete_confirm'), + deleted: t('components.manageable_authenticator.deleted'), + manage_accessible_label: t('components.manageable_authenticator.manage_accessible_label'), + } + end +end diff --git a/app/components/manageable_authenticator_component.scss b/app/components/manageable_authenticator_component.scss new file mode 100644 index 00000000000..06f93a31177 --- /dev/null +++ b/app/components/manageable_authenticator_component.scss @@ -0,0 +1,77 @@ +@use 'uswds-core' as *; + +.manageable-authenticator__edit { + @include u-margin-y(1.5); + @include u-padding(2); + @include u-border(1px, 'primary-light'); + @include u-radius('lg'); + display: none; + + .manageable-authenticator--editing & { + display: block; + } + + .manageable-authenticator--deleted & { + @include u-padding(0); + @include u-border(0); + } + + &:focus:not(:focus-visible) { + outline: none; + } +} + +.manageable-authenticator__alert { + @include u-margin-bottom(2); + display: none; + + .manageable-authenticator--alert-visible & { + display: block; + } + + &:focus:not(:focus-visible) { + outline: none; + } +} + +.manageable-authenticator__rename { + display: none; + + .manageable-authenticator--renaming & { + display: block; + } +} + +.manageable-authenticator__details { + .manageable-authenticator--renaming &, + .manageable-authenticator--deleted & { + display: none; + } +} + +.manageable-authenticator__details-name { + display: block; +} + +.manageable-authenticator__summary { + @include grid-row; + @include u-padding(1); + @include u-border(1px, 'primary-light'); + + lg-manageable-authenticator + lg-manageable-authenticator & { + border-top: none; + } + + .manageable-authenticator--editing &, + .manageable-authenticator--deleted & { + display: none; + } +} + +.manageable-authenticator__summary-name { + @include grid-col('fill'); +} + +.manageable-authenticator__actions { + @include grid-col('auto'); +} diff --git a/app/components/manageable_authenticator_component.ts b/app/components/manageable_authenticator_component.ts new file mode 100644 index 00000000000..ee0d8a02a22 --- /dev/null +++ b/app/components/manageable_authenticator_component.ts @@ -0,0 +1 @@ +import '@18f/identity-manageable-authenticator/manageable-authenticator-element'; diff --git a/app/components/spinner_button_component.html.erb b/app/components/spinner_button_component.html.erb index 8c02b6c5890..40999acf2ca 100644 --- a/app/components/spinner_button_component.html.erb +++ b/app/components/spinner_button_component.html.erb @@ -1,4 +1,10 @@ -<%= content_tag(:'lg-spinner-button', class: css_class, 'spin-on-click': spin_on_click) do %> +<%= content_tag( + :'lg-spinner-button', + **wrapper_options, + class: css_class, + 'spin-on-click': spin_on_click, + 'long-wait-duration-ms': long_wait_duration.in_milliseconds, + ) do %> <%= render ButtonComponent.new(type: :button, **button_options) do %> <%= content %>