From 9279aedd00b59ff533b58e0aa9ceeb60d0a92b54 Mon Sep 17 00:00:00 2001 From: Cameron Dutro Date: Thu, 22 Aug 2024 10:03:27 -0700 Subject: [PATCH] Add a Rails migration guide for SelectMenu -> SelectPanel (#837) * Add a Rails migration guide for SelectMenu -> SelectPanel * Update content/guides/rails/migration-guides/primer-css-select-menu.mdx Co-authored-by: Joyce Zhu * Update content/guides/rails/migration-guides/primer-css-select-menu.mdx Co-authored-by: Joyce Zhu * Add link to docs as suggested by @joycezhu --------- Co-authored-by: Joyce Zhu --- content/deprecated-components/select-menu.mdx | 2 +- .../guides/rails/migration-guides/index.mdx | 13 +- .../primer-css-select-menu.mdx | 117 ++++++++++++++++++ 3 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 content/guides/rails/migration-guides/primer-css-select-menu.mdx diff --git a/content/deprecated-components/select-menu.mdx b/content/deprecated-components/select-menu.mdx index 68541bddb..8f0dafa77 100644 --- a/content/deprecated-components/select-menu.mdx +++ b/content/deprecated-components/select-menu.mdx @@ -11,7 +11,7 @@ export default ComponentLayout Usage for this component is not encouraged - Instead, please see action menu. + Instead, please see action menu or select panel. Select menu is only available right now as a [CSS component](/deprecated-components/select-menu/css). Select menu can make use of JavaScript-enabled live filtering, selected states, tabbed lists, and keyboard navigation with a bit of markup. To see all examples of this component, view it in the [Primer CSS storybook](https://primer.style/css/storybook/?path=/docs/deprecated-selectmenu--docs). \ No newline at end of file diff --git a/content/guides/rails/migration-guides/index.mdx b/content/guides/rails/migration-guides/index.mdx index 71ac745c2..9015409ac 100644 --- a/content/guides/rails/migration-guides/index.mdx +++ b/content/guides/rails/migration-guides/index.mdx @@ -14,12 +14,13 @@ stable status. | Primer CSS Class | ViewComponent | Guide | |------------------|---------------|-------| -| `State` | [`Primer::Beta::State`](/components/state-label/rails/beta) | | -| `breadcrumb-item` | [`Primer::Beta::Breadcrumbs`](/components/breadcrumbs/rails/beta) | | -| `Counter` | [`Primer::Beta::Counter`](/components/counter-label/rails/beta) | | -| `Subhead` | [`Primer::Beta::Subhead`](/components/pagehead/rails/beta) | | -| `blankslate` | [`Primer::Beta::Blankslate`](/components/blankslate/rails/beta) | | -| [`.tooltipped`](https://primer.style/css/storybook/?path=/docs/deprecated-tooltip--docs) | [`Primer::Alpha::Tooltip`](/components/tooltip/rails/alpha) and other options | [Migrating from `.tooltipped`](/guides/rails/migration-guides/primer-css-tooltipped) | +| `State` | [`Primer::Beta::State`](/components/state-label/rails/beta) | | +| `breadcrumb-item` | [`Primer::Beta::Breadcrumbs`](/components/breadcrumbs/rails/beta) | | +| `Counter` | [`Primer::Beta::Counter`](/components/counter-label/rails/beta) | | +| `Subhead` | [`Primer::Beta::Subhead`](/components/pagehead/rails/beta) | | +| `blankslate` | [`Primer::Beta::Blankslate`](/components/blankslate/rails/beta) | | +| [`.tooltipped`](https://primer.style/css/storybook/?path=/docs/deprecated-tooltip--docs) | [`Primer::Alpha::Tooltip`](/components/tooltip/rails/alpha) and other options | [Migrating from `.tooltipped`](/guides/rails/migration-guides/primer-css-tooltipped) | +| [`SelectMenu`](https://primer.style/css/storybook/?path=/docs/deprecated-selectmenu--docs) | [`Primer::Alpha::SelectPanel`](/components/selectpanel/rails/alpha) | [Migrating from `SelectMenu`](/guides/rails/migration-guides/primer-css-select-menu) | ## Upgrading deprecated components diff --git a/content/guides/rails/migration-guides/primer-css-select-menu.mdx b/content/guides/rails/migration-guides/primer-css-select-menu.mdx new file mode 100644 index 000000000..85ece952e --- /dev/null +++ b/content/guides/rails/migration-guides/primer-css-select-menu.mdx @@ -0,0 +1,117 @@ +--- +title: SelectMenu migration +--- + +import InlineCode from '@primer/gatsby-theme-doctocat/src/components/inline-code' + +The Primer `SelectPanel` component in Primer ViewComponents should be used in place of the `SelectMenu` component from Primer CSS. It provides a number of accessibility improvements over the CSS component, supports multiple data fetching strategies, customizable filtering behavior, and more. + +### The show button + +`SelectMenu`s are powered by two native elements. The `
` element contains the list of menu items while the `` element typically contains a button that opens the menu. + +`SelectPanel`s work in a similar fashion. Rather than a `` element however, `SelectPanel`s use the `show_button` slot, which renders a Primer button. + +```erb +<%= render(Primer::Alpha::SelectPanel.new) do |panel| %> + <% panel.with_show_button { "Click me" } %> +<% end %> +``` + +Panels can also be opened in JavaScript by calling the `show()` method. + +```javascript +document.querySelector('select-panel').show(); +``` + +### Panels are dialogs + +`SelectMenu`s wrap their ``/`
` elements inside the [``](https://github.com/github/details-dialog-element) element to display their items in a modal dialog. `SelectPanel`s have this functionality built in, i.e. there is no need to wrap them in any additional elements. + +`SelectPanel`s are anchored to their show buttons. In other words, they appear attached to their show buttons and act like menus or popovers. However, `SelectPanel`s use the native `` element under the hood and therefore exhibit [dialog behavior](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/) -- they trap focus, prevent scrolling content under the dialog, etc. `SelectMenu`s by contrast are not backed by dialogs; they do not trap focus and do not prevent page scrolling. + +### Choosing a fetch strategy + +It is common for usages of `SelectMenu` to fetch list items from a remote server using the `` or `` elements. `SelectPanel`s also use these elements, but do so automatically "under the hood" depending on the configured fetch strategy. + +| Element | Fetch strategy | Description | +|-------------------------------------------------------------------|-------------------------|---------------------------------------------------------------------------------------------| +| `` | `:remote` (default) | Items are fetched from the server whenever the user types into the filter input text field. | +| `` | `:eventually_local` | Items are fetched once when the panel opens for the first time. | +| None, i.e. a static list | `:local` | No remote requests are made. | + +For the `:remote` and `:eventually_local` fetch strategies, items are fetched using the URL provided in the `src:` argument. + +```erb +<%= render(Primer::Alpha::SelectPanel.new( + fetch_strategy: :remote, # this is the default, shown here for demo purposes + src: probably_some_rails_path_helper +)) %> +``` + +### Rendering list items + +When rendering a static list (i.e. using the `:local` fetch strategy), use the `item` slot to define list items. `label:` is the only required argument. + +```erb +<%= render(Primer::Alpha::SelectPanel.new) do |panel| %> + <% panel.with_item(label: "My item") %> +<% end %> +``` + +When rendering a dynamic list where items are fetched from a remote server, the `SelectPanel` component expects remote responses to render instances of `Primer::Alpha::SelectPanel::ItemList`. The `ItemList` component is the same component that static `SelectPanel`s use to render list items. + +```erb +<%= render(Primer::Alpha::SelectPanel::ItemList.new) do |list| %> + <% list.with_item(label: "My item") %> +<% end %> +``` + +#### HTML fragments + +Note that both the `` and `` elements that `SelectPanel` uses under the hood require that responses have a content type of `text/fragment+html`. See the `SelectPanel` [docs](/components/selectpanel/rails/alpha) for details. + +### Choosing a select variant + +`SelectPanel`s automatically manage list item state, including which items are checked (or "active" in `SelectPanel` and `ActionList` parlance). Items can be marked as checked by passing `active: true` to the item's constructor. + +```erb +<%= render(Primer::Alpha::SelectPanel.new) do |panel| %> + <% panel.with_item(label: "My item", active: true) %> +<% end %> +``` + +In addition to supporting single-select (the default), `SelectPanel`s also support multi-select behavior. Selection behavior can be controlled via the `select_variant:` argument. Options are `:single` (the default) and `:multiple`. + +```erb +<%= render(Primer::Alpha::SelectPanel.new( + select_variant: :multiple +)) %> +``` + + +When rendering panels and their corresponding dynamic list items, make sure to pass the same {"select_variant:"} argument to both {"SelectPanel.new"} and {"SelectPanel::ItemList.new"}, or you may experience unexpected behavior. + + +#### Identifying list items + +When using the `:remote` fetch strategy in combination with single- or multi-select modes, list items must be uniquely identifiable via the `value:` argument so the component can "remember" which list items the user has selected. This is important because the component needs a mechanism by which it can reconcile items selected in the client and items the server reports as selected. Neglecting to provide `value`s can lead to unexpected selection behavior. + +```erb +<%= render(Primer::Alpha::SelectPanel.new( + select_variant: :multiple, + fetch_strategy: :remote +)) do |panel| %> + <% panel.with_item(label: "Apples", content_arguments: { data: { value: "apples" } }) %> + <% panel.with_item(label: "Bananas", content_arguments: { data: { value: "bananas" } }) %> +<% end %> +``` + +### Loading behavior + +The `SelectPanel` component automatically displays `Spinner`s during initial and subsequent remote fetches and announces to screen reader users when requests have succeeded or failed. There is no need to render custom spinners or make custom announcements as must often be done for `SelectMenu`s. + +### Where to go next + +1. The official [SelectPanel docs](/components/selectpanel/rails/alpha) +1. Have questions? Hop into the #primer-rails Slack channel.