-
diff --git a/src/components/UI/FullScreenDialog/__snapshots__/FullScreenDialog.test.js.snap b/src/components/UI/FullScreenDialog/__snapshots__/FullScreenDialog.test.js.snap
index ddda25d1d..736e65312 100644
--- a/src/components/UI/FullScreenDialog/__snapshots__/FullScreenDialog.test.js.snap
+++ b/src/components/UI/FullScreenDialog/__snapshots__/FullScreenDialog.test.js.snap
@@ -8,6 +8,7 @@ exports[`FullScreenDialog tests default renderer 1`] = `
"container": "FullScreenDialog-container-3",
"containerDark": "FullScreenDialog-containerDark-4",
"content": "FullScreenDialog-content-5",
+ "contentFullHeight": "FullScreenDialog-contentFullHeight-7",
"contentFullWidth": "FullScreenDialog-contentFullWidth-6",
"title": "FullScreenDialog-title-2",
}
diff --git a/src/components/UI/TonalChip/TonalChip.js b/src/components/UI/TonalChip/TonalChip.js
new file mode 100644
index 000000000..5e316b3f1
--- /dev/null
+++ b/src/components/UI/TonalChip/TonalChip.js
@@ -0,0 +1,102 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { alpha, makeStyles } from '@material-ui/core/styles';
+import Chip from '@material-ui/core/Chip';
+
+// Softer corner than the default 4px, matching the app's card surfaces.
+const chipRadius = (theme) => (theme.shape.borderRadius || 4) * 2;
+
+/**
+ * A tinted ground with a same-hue label. The label takes the dark or light
+ * shade of the hue (whichever the theme needs) so it keeps contrast on the
+ * tint; a plain color string is accepted for neutral tones.
+ */
+const tone = (theme, color) => {
+ const isDark = theme.palette.type === 'dark';
+ const main = color.main || color;
+ const label = (isDark ? color.light : color.dark) || main;
+
+ return {
+ color: label,
+ backgroundColor: alpha(main, isDark ? 0.22 : 0.11),
+ borderColor: alpha(main, isDark ? 0.42 : 0.26)
+ };
+};
+
+const useStyles = makeStyles((theme) => ({
+ // MUI injects its own .MuiChip-outlined rules after component styles, so a
+ // single-class override loses the cascade and the chip falls back to the
+ // default grey outline. Doubling the selector settles it by specificity.
+ chip: {
+ '&&': {
+ height: 24,
+ borderRadius: chipRadius(theme),
+ borderStyle: 'solid',
+ borderWidth: 1,
+ fontSize: '0.75rem',
+ fontWeight: 600,
+ letterSpacing: '0.02em'
+ },
+ '& .MuiChip-label': {
+ paddingLeft: theme.spacing(0.75),
+ paddingRight: theme.spacing(1)
+ },
+ '& .MuiChip-icon': {
+ color: 'inherit',
+ fontSize: '0.9375rem',
+ marginLeft: theme.spacing(0.75),
+ marginRight: 0
+ }
+ },
+ withoutIcon: {
+ '& .MuiChip-label': {
+ paddingLeft: theme.spacing(1)
+ }
+ },
+ primary: { '&&': tone(theme, theme.palette.primary) },
+ secondary: { '&&': tone(theme, theme.palette.secondary) },
+ success: { '&&': tone(theme, theme.palette.success || { main: '#369b00' }) },
+ neutral: { '&&': tone(theme, theme.palette.text.secondary) }
+}));
+
+/**
+ * The app's badge: a tinted ground with a same-hue label, sized to sit inside a
+ * row of text. Chips go through here so a count and a status never read as two
+ * different components.
+ */
+const TonalChip = ({ tone, icon, label, title, className }) => {
+ const classes = useStyles();
+
+ return (
+
+ );
+};
+
+TonalChip.defaultProps = {
+ tone: 'primary'
+};
+
+TonalChip.propTypes = {
+ tone: PropTypes.oneOf(['primary', 'secondary', 'success', 'neutral']),
+ icon: PropTypes.element,
+ label: PropTypes.node.isRequired,
+ title: PropTypes.string,
+ className: PropTypes.string
+};
+
+export default TonalChip;
diff --git a/src/components/UI/TonalChip/TonalChip.test.js b/src/components/UI/TonalChip/TonalChip.test.js
new file mode 100644
index 000000000..fd652c05a
--- /dev/null
+++ b/src/components/UI/TonalChip/TonalChip.test.js
@@ -0,0 +1,77 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import { MuiThemeProvider, createTheme } from '@material-ui/core/styles';
+import HomeIcon from '@material-ui/icons/Home';
+
+import TonalChip from './TonalChip';
+
+const render = (props = {}, theme = createTheme()) =>
+ mount(
+
+
+
+ );
+
+const cssFor = (element) => {
+ const names = element.className.split(' ');
+ return Array.from(document.styleSheets)
+ .reduce(
+ (rules, sheet) => rules.concat(Array.from(sheet.cssRules || [])),
+ []
+ )
+ .filter(
+ (rule) =>
+ rule.selectorText &&
+ names.some((name) => rule.selectorText === `.${name}.${name}`)
+ )
+ .map((rule) => ({
+ // cssstyle only exposes a subset of properties as getters; go through
+ // getPropertyValue so shorthands like background-color come back.
+ background: rule.style.getPropertyValue('background-color'),
+ border: rule.style.getPropertyValue('border-color'),
+ color: rule.style.getPropertyValue('color')
+ }));
+};
+
+describe('TonalChip', () => {
+ it('renders its label and icon', () => {
+ const wrapper = render({ icon:
});
+ expect(wrapper.text()).toContain('Root');
+ expect(wrapper.find(HomeIcon)).toHaveLength(1);
+ });
+
+ it('names itself after the full phrase when the label is abbreviated', () => {
+ const chip = render({ label: 'Root', title: 'Root Board' })
+ .find('.MuiChip-root')
+ .first();
+ expect(chip.prop('aria-label')).toBe('Root Board');
+ });
+
+ it('tints the chip with the tone, at a specificity MUI cannot override', () => {
+ const chip = render({ tone: 'success' }).find('.MuiChip-root').first();
+ const declarations = cssFor(chip.getDOMNode());
+ const tinted = declarations.filter((style) => style.background);
+ expect(tinted).not.toHaveLength(0);
+ tinted.forEach((style) => {
+ expect(style.background).not.toBe('transparent');
+ expect(style.border).toBeTruthy();
+ });
+ });
+
+ it('adapts the label shade to a dark theme', () => {
+ const shadeFor = (theme) =>
+ cssFor(
+ render({ tone: 'success' }, theme)
+ .find('.MuiChip-root')
+ .first()
+ .getDOMNode()
+ )
+ .map((style) => style.color)
+ .filter(Boolean);
+
+ const light = shadeFor(createTheme());
+ const dark = shadeFor(createTheme({ palette: { type: 'dark' } }));
+ expect(light).not.toEqual([]);
+ expect(dark).not.toEqual(light);
+ });
+});
diff --git a/src/components/UI/TonalChip/index.js b/src/components/UI/TonalChip/index.js
new file mode 100644
index 000000000..c76d4f39f
--- /dev/null
+++ b/src/components/UI/TonalChip/index.js
@@ -0,0 +1 @@
+export { default } from './TonalChip';
diff --git a/src/translations/src/cboard.json b/src/translations/src/cboard.json
index b849c389d..7ea67cd61 100644
--- a/src/translations/src/cboard.json
+++ b/src/translations/src/cboard.json
@@ -288,22 +288,15 @@
"cboard.components.CommunicatorToolbar.SyncButton.saving": "Saving",
"cboard.components.CommunicatorToolbar.SyncButton.synced": "Synced",
"cboard.components.CommunicatorDialog.title": "My Communicator",
- "cboard.components.CommunicatorDialog.menu": "Menu",
"cboard.components.CommunicatorDialog.menuRootBoardOption": "Set as Root Board",
"cboard.components.CommunicatorDialog.menuPublishOption": "Publish Board",
"cboard.components.CommunicatorDialog.menuUnpublishOption": "Unpublish Board",
"cboard.components.CommunicatorDialog.emptyBoardsList": "No boards",
- "cboard.components.CommunicatorDialog.loadNextPage": "Load More",
"cboard.components.CommunicatorDialog.search": "Search",
"cboard.components.CommunicatorDialog.author": "By {author}",
- "cboard.components.CommunicatorDialog.addBoard": "Add Board",
- "cboard.components.CommunicatorDialog.removeBoard": "Remove Board",
- "cboard.components.CommunicatorDialog.communicatorBoards": "Boards",
"cboard.components.CommunicatorDialog.allBoards": "Public Boards",
"cboard.components.CommunicatorDialog.myBoards": "All My Boards",
"cboard.components.CommunicatorDialog.boardsQty": "{qty} boards",
- "cboard.components.CommunicatorDialog.helpAndSupport": "Help And Support",
- "cboard.components.CommunicatorDialog.termsOfService": "Terms Of Service",
"cboard.components.CommunicatorDialog.tilesQty": "{qty} Tiles",
"cboard.components.CommunicatorDialog.boardInfo": "Board Information",
"cboard.components.CommunicatorDialog.boardAddedToCommunicator": "Board successfully added to your Communicator",
@@ -313,7 +306,6 @@
"cboard.components.CommunicatorDialog.boardInfoAuthor": "Board Author",
"cboard.components.CommunicatorDialog.boardInfoTiles": "Number of tiles",
"cboard.components.CommunicatorDialog.boardInfoId": "Board ID",
- "cboard.components.CommunicatorDialog.boardInfoUrl": "Board URL",
"cboard.components.CommunicatorDialog.boardInfoDate": "Last edited",
"cboard.components.CommunicatorDialog.boardDescription": "Description",
"cboard.components.CommunicatorDialog.boardPublished": "Board successfully published! Everybody can see your board now.",
@@ -330,8 +322,6 @@
"cboard.components.CommunicatorDialog.publishBoard": "Board Description",
"cboard.components.CommunicatorDialog.publishBoardDescription": "Please provide a short description about the content of the board and what is the board intended for.",
"cboard.components.CommunicatorDialog.accept": "Accept",
- "cboard.components.CommunicatorDialog.imageBoard": "Edit Board Image",
- "cboard.components.CommunicatorDialog.imageBoardDescription": "You can either upload or look for symbols from libraries.",
"cboard.components.CommunicatorDialog.imageSearch": "Search for symbols",
"cboard.components.CommunicatorDialog.editBoardTitle": "Edit board name and description",
"cboard.components.CommunicatorDialog.editBoardTitleDescription": "You can edit board name and description from here. Please consider to write a meaningful description for your board.",
@@ -353,8 +343,6 @@
"cboard.components.CommunicatorDialog.walkthroughPublicBoardsCopy": "Copy the public board and add it into your communicator. This option will create a copy of the public board, and it will not modify the original board.",
"cboard.components.CommunicatorDialog.walkthroughPublicBoardsDetail": "This buttton will display detailed board information.",
"cboard.components.CommunicatorDialog.walkthroughAllMyBoardsRemoveBoard": "Remove board from the communicator, but it will not delete the board from the database and it will be still available under All My Boards tab.",
- "cboard.components.CommunicatorDialog.walkthroughAllMyBoardsEditBoardImage": "You can edit the image that represents the board.",
- "cboard.components.CommunicatorDialog.walkthroughAllMyBoardsEditBoardName": "You can edit the board title and description.",
"cboard.components.CommunicatorDialog.walkthroughAllMyBoardsAddBoard": "Add board into the communicator.",
"cboard.components.CommunicatorDialog.walkthroughAllMyBoardsPublishBoard": "Publish board: Make your board public. The board will be displayed under the Public Boards tab.",
"cboard.components.CommunicatorDialog.walkthroughAllMyBoardsUnpublishBoard": "Unpublish board: Make your board private. The board won't be displayed under the Public Boards tab.",
@@ -368,6 +356,48 @@
"cboard.components.CommunicatorDialog.boardReportSuccesGratitude": "Thanks for helping Cboard to be out of inappropriate content.",
"cboard.components.CommunicatorDialog.boardReportClose": "Close",
"cboard.components.CommunicatorDialog.boardInfoLocale": "Language",
+ "cboard.components.CommunicatorDialog.boardCopyError": "ERROR: There was an error trying to get the board.",
+ "cboard.components.CommunicatorDialog.sectionMyBoards": "My Boards",
+ "cboard.components.CommunicatorDialog.sectionMyBoardsHint": "All the boards you have created",
+ "cboard.components.CommunicatorDialog.sectionCommunity": "Community",
+ "cboard.components.CommunicatorDialog.sectionCommunityHint": "Discover and copy public boards",
+ "cboard.components.CommunicatorDialog.navigation": "Navigation",
+ "cboard.components.CommunicatorDialog.expandNavigation": "Expand navigation",
+ "cboard.components.CommunicatorDialog.collapseNavigation": "Collapse navigation",
+ "cboard.components.CommunicatorDialog.searchBoardsPlaceholder": "Search by name or author",
+ "cboard.components.CommunicatorDialog.clearSearch": "Clear search",
+ "cboard.components.CommunicatorDialog.gridView": "Grid view",
+ "cboard.components.CommunicatorDialog.listView": "List view",
+ "cboard.components.CommunicatorDialog.openBoardInNewTab": "Open in a new tab",
+ "cboard.components.CommunicatorDialog.errorGettingBoards": "There was an error loading the boards.",
+ "cboard.components.CommunicatorDialog.tryAgain": "Try again",
+ "cboard.components.CommunicatorDialog.noBoardsFound": "No boards match your search.",
+ "cboard.components.CommunicatorDialog.sectionQuickAccess": "Quick access",
+ "cboard.components.CommunicatorDialog.sectionQuickAccessHint": "Boards you can reach from the communicator bar",
+ "cboard.components.CommunicatorDialog.quickAccessExplainer": "These {qty} boards appear in your communicator bar. They all live in My Boards.",
+ "cboard.components.CommunicatorDialog.quickAccessEmpty": "Your communicator bar is empty.",
+ "cboard.components.CommunicatorDialog.quickAccessAddFromMyBoards": "Add a board from My Boards",
+ "cboard.components.CommunicatorDialog.addToQuickAccess": "Add to quick access",
+ "cboard.components.CommunicatorDialog.removeFromQuickAccess": "Remove from quick access",
+ "cboard.components.CommunicatorDialog.boardOptions": "Board options",
+ "cboard.components.CommunicatorDialog.moveUp": "Move up",
+ "cboard.components.CommunicatorDialog.moveDown": "Move down",
+ "cboard.components.CommunicatorDialog.boardMoved": "{name} moved to position {position} of {total}",
+ "cboard.components.CommunicatorDialog.boardDetails": "Board details",
+ "cboard.components.CommunicatorDialog.selectBoardPrompt": "Select a board to see its details and actions",
+ "cboard.components.CommunicatorDialog.closeDetails": "Close details",
+ "cboard.components.CommunicatorDialog.showBoard": "Show",
+ "cboard.components.CommunicatorDialog.cloneBoard": "Clone",
+ "cboard.components.CommunicatorDialog.cloneBoardUnavailable": "Cloning is not available yet",
+ "cboard.components.CommunicatorDialog.exportBoard": "Export",
+ "cboard.components.CommunicatorDialog.exportBoardToPdf": "Save as PDF",
+ "cboard.components.CommunicatorDialog.exportingBoard": "Exporting {name}…",
+ "cboard.components.CommunicatorDialog.boardExported": "{name} exported",
+ "cboard.components.CommunicatorDialog.boardExportError": "Could not export {name}",
+ "cboard.components.CommunicatorDialog.statusPublic": "Public",
+ "cboard.components.CommunicatorDialog.statusPrivate": "Private",
+ "cboard.components.CommunicatorDialog.statusRoot": "Root",
+ "cboard.components.CommunicatorDialog.statusActive": "Active",
"cboard.components.Board.EditToolbar.deleteTileTitle": "Delete Tiles",
"cboard.components.Board.EditToolbar.deleteTileDescription": "Are you sure you want to delete the tile(s)?",
"cboard.components.Board.EditToolbar.deleteTileCancel": "Cancel",
@@ -3979,7 +4009,6 @@
"cboard.components.BoardShare.cannotShare": "Cannot share the board on facebook. Please try again later.",
"cboard.components.SymbolSearch.cboardSymbols": "Cboard Symbols",
"cboard.components.SymbolSearch.inclusivityOptions": "Inclusivity (ARASAAC & Cboard)",
- "cboard.components.CommunicatorDialog.boardCopyError": "ERROR: There was an error trying to get the board.",
"cboard.components.CommunicatorDialog.boardInfoLanguage": "Language",
"cboard.components.CommunicatorToolbar.copyMessage": "Copied to clipboard!",
"cboard.components.OfflineNotification.offlineTitle": "You're Offline",