From 101a5ee0c5f38990f75e26213260b3bc1d2e8ca6 Mon Sep 17 00:00:00 2001 From: Josh Brown Date: Tue, 13 Feb 2024 15:19:11 +0000 Subject: [PATCH] Add notification UI (#449) * Add notification UI * Make animation an aribtrary value It didn't make much sense in the theme as it feels very specific, this change also means the timing information is clearer within the html itself. * Update to use tailwind theme * Refactor structure of icon * Add support for multiple notifications at once * Adjust notification animation timing * Make notification more accessible Applies role to the notification which will apply the appropriate aria-live status to ensure notifications are read out when they are rendered into the screen for screenreader users. Wraps the svg with a button tag that keyboard users can focus and engage with to close the notification. * Fix notification progress indicator placement * Map flash types to notification types automatically * Refine notification animations * Set success as default icon for notifications --- app/helpers/application_helper.rb | 7 ++++++ .../controllers/element_removal_controller.js | 9 ++++++++ app/views/layouts/application.html.erb | 3 +++ app/views/shared/_notification.html.erb | 23 +++++++++++++++++++ config/tailwind.config.js | 9 ++++++++ 5 files changed, 51 insertions(+) create mode 100644 app/javascript/controllers/element_removal_controller.js create mode 100644 app/views/shared/_notification.html.erb diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index db5c27b2..3e01dea3 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -11,6 +11,13 @@ module ApplicationHelper name.underscore end + def notification(text, **options, &block) + content = tag.p(text) + content = capture &block if block_given? + + render partial: "shared/notification", locals: { type: options[:type], content: content } + end + # Wrap view with <%= modal do %> ... <% end %> to have it open in a modal # Make sure to add data-turbo-frame="modal" to the link/button that opens the modal def modal(&block) diff --git a/app/javascript/controllers/element_removal_controller.js b/app/javascript/controllers/element_removal_controller.js new file mode 100644 index 00000000..b14906a9 --- /dev/null +++ b/app/javascript/controllers/element_removal_controller.js @@ -0,0 +1,9 @@ +import { Controller } from '@hotwired/stimulus' + +// Connects to data-controller="element-removal" +export default class extends Controller { + remove() { + this.element.remove() + } +} + diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 503af69a..780e9bf8 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -24,6 +24,9 @@ +
+ <%= safe_join(flash.map { |type, message| notification(message, type: type) }) %> +
diff --git a/app/views/shared/_notification.html.erb b/app/views/shared/_notification.html.erb new file mode 100644 index 00000000..df31010d --- /dev/null +++ b/app/views/shared/_notification.html.erb @@ -0,0 +1,23 @@ +<%# locals: (type: "success", content:) -%> + + + + diff --git a/config/tailwind.config.js b/config/tailwind.config.js index 10b6a592..47779fa2 100644 --- a/config/tailwind.config.js +++ b/config/tailwind.config.js @@ -204,6 +204,15 @@ module.exports = { fontSize: { "2xs": ".625rem", }, + keyframes: { + 'appear-then-fade': { + '0%,100%': { opacity: 0 }, + '5%,90%': { opacity: 1 }, + }, + 'stroke-fill': { + to: { 'stroke-dashoffset': 0 }, + } + } }, }, plugins: [