← All changes
|
js/components/ManageMenu/CommunityCloud/CloudSnippetsTable.tsx
+41
-10
3.10.2
→
4.0.0-beta.2
View file →
| @@ -63,14 +63,21 @@ | ||
| 63 | 63 | selectedIds: Set<CloudSnippetSchema['id']> |
| 64 | 64 | setSelectedIds: Dispatch<SetStateAction<Set<CloudSnippetSchema['id']>>> |
| 65 | 65 | } |
| 66 | 66 | |
| 67 | +/** | |
| 68 | + * Selects every snippet on the page that can be downloaded. With nothing | |
| 69 | + * downloadable the box is disabled and unticked: "all of nothing" must not | |
| 70 | + * read as a selection. | |
| 71 | + */ | |
| 67 | 72 | const TableHeadingCheckbox: React.FC<TableHeadingCheckboxProps> = ({ availableIds, selectedIds, setSelectedIds }) => |
| 68 | 73 | <td className="column-cb check-column"> |
| 69 | 74 | <input |
| 70 | 75 | id="cb-select-all-cloud-snippets" |
| 71 | 76 | type="checkbox" |
| 72 | - checked={availableIds.every(snippetId => selectedIds.has(snippetId))} | |
| 77 | + checked={0 < availableIds.length && availableIds.every(snippetId => selectedIds.has(snippetId))} | |
| 78 | + disabled={0 === availableIds.length} | |
| 79 | + title={0 === availableIds.length ? __('Nothing on this page can be downloaded.', 'code-snippets') : undefined} | |
| 73 | 80 | onChange={event => |
| 74 | 81 | setSelectedIds(previous => |
| 75 | 82 | new Set(event.target.checked |
| 76 | 83 | ? [...previous, ...availableIds] |
| @@ -75,12 +82,23 @@ | ||
| 75 | 82 | new Set(event.target.checked |
| 76 | 83 | ? [...previous, ...availableIds] |
| 77 | 84 | : [...previous].filter(snippetId => !availableIds.includes(snippetId))) |
| 78 | 85 | )} |
| 79 | - aria-label={__('Select all snippets', 'code-snippets')} | |
| 86 | + aria-label={__('Select all downloadable snippets', 'code-snippets')} | |
| 80 | 87 | /> |
| 81 | 88 | </td> |
| 82 | 89 | |
| 90 | +/** Why a snippet cannot be selected for download, or undefined when it can. */ | |
| 91 | +const unavailableReason = (snippet: CloudSnippetSchema): string | undefined => { | |
| 92 | + if (snippet.local_id) { | |
| 93 | + return __('Already in your library.', 'code-snippets') | |
| 94 | + } | |
| 95 | + | |
| 96 | + return isCloudSnippetDownloadable(snippet) | |
| 97 | + ? undefined | |
| 98 | + : __('Requires Code Snippets Pro.', 'code-snippets') | |
| 99 | +} | |
| 100 | + | |
| 83 | 101 | interface TableRowCheckboxProps { |
| 84 | 102 | snippet: CloudSnippetSchema |
| 85 | 103 | selected: Set<CloudSnippetSchema['id']> |
| 86 | 104 | setSelected: Dispatch<SetStateAction<Set<CloudSnippetSchema['id']>>> |
| @@ -85,18 +103,29 @@ | ||
| 85 | 103 | selected: Set<CloudSnippetSchema['id']> |
| 86 | 104 | setSelected: Dispatch<SetStateAction<Set<CloudSnippetSchema['id']>>> |
| 87 | 105 | } |
| 88 | 106 | |
| 89 | -const TableRowCheckbox: React.FC<TableRowCheckboxProps> = ({ snippet, selected, setSelected }) => | |
| 90 | - <th scope="row" className="check-column"> | |
| 91 | - {isCloudSnippetDownloadable(snippet) && ( | |
| 107 | +/** | |
| 108 | + * Every row shows a box, so the column reads as one control: a snippet that | |
| 109 | + * cannot be downloaded gets a disabled box that says why. | |
| 110 | + */ | |
| 111 | +const TableRowCheckbox: React.FC<TableRowCheckboxProps> = ({ snippet, selected, setSelected }) => { | |
| 112 | + const reason = unavailableReason(snippet) | |
| 113 | + | |
| 114 | + return ( | |
| 115 | + <th scope="row" className="check-column"> | |
| 92 | 116 | <input |
| 93 | 117 | id={`cb-select-${snippet.id}`} |
| 94 | 118 | type="checkbox" |
| 95 | 119 | name="checked[]" |
| 96 | - checked={selected.has(snippet.id)} | |
| 97 | - // translators: %s: snippet name. | |
| 98 | - aria-label={sprintf(__('Select %s', 'code-snippets'), snippet.name)} | |
| 120 | + checked={!reason && selected.has(snippet.id)} | |
| 121 | + disabled={!!reason} | |
| 122 | + title={reason} | |
| 123 | + aria-label={reason | |
| 124 | + // translators: 1: snippet name, 2: why it cannot be selected. | |
| 125 | + ? sprintf(__('%1$s cannot be selected: %2$s', 'code-snippets'), snippet.name, reason) | |
| 126 | + // translators: %s: snippet name. | |
| 127 | + : sprintf(__('Select %s', 'code-snippets'), snippet.name)} | |
| 99 | 128 | onChange={event => |
| 100 | 129 | setSelected(previous => |
| 101 | 130 | new Set(event.target.checked |
| 102 | 131 | ? [...previous, snippet.id] |
| @@ -101,10 +130,12 @@ | ||
| 101 | 130 | new Set(event.target.checked |
| 102 | 131 | ? [...previous, snippet.id] |
| 103 | 132 | : [...previous].filter(snippetId => snippetId !== snippet.id)) |
| 104 | 133 | )} |
| 105 | - />)} | |
| 106 | - </th> | |
| 134 | + /> | |
| 135 | + </th> | |
| 136 | + ) | |
| 137 | +} | |
| 107 | 138 | |
| 108 | 139 | export interface CloudSnippetsTableProps { |
| 109 | 140 | snippets: CloudSnippetSchema[] |
| 110 | 141 | selected?: Set<CloudSnippetSchema['id']> |