diff --git a/website/src/styles/tippy/tippy.css b/website/src/styles/tippy/tippy.css index c8aea0eb7f..e25ceace03 100644 --- a/website/src/styles/tippy/tippy.css +++ b/website/src/styles/tippy/tippy.css @@ -35,7 +35,7 @@ .tippy-popper[x-placement^='top'] .tippy-arrow { border-top: 8px solid #333; - border-top: 8px solid var(--gray); + border-top: 8px solid var(--tooltip-bg); border-right: 8px solid transparent; border-left: 8px solid transparent; bottom: -7px; @@ -122,7 +122,7 @@ .tippy-popper[x-placement^='bottom'] .tippy-arrow { border-bottom: 8px solid #333; - border-bottom: 8px solid var(--gray); + border-bottom: 8px solid var(--tooltip-bg); border-right: 8px solid transparent; border-left: 8px solid transparent; top: -7px; @@ -209,7 +209,7 @@ .tippy-popper[x-placement^='left'] .tippy-arrow { border-left: 8px solid #333; - border-left: 8px solid var(--gray); + border-left: 8px solid var(--tooltip-bg); border-top: 8px solid transparent; border-bottom: 8px solid transparent; right: -7px; @@ -296,7 +296,7 @@ .tippy-popper[x-placement^='right'] .tippy-arrow { border-right: 8px solid #333; - border-right: 8px solid var(--gray); + border-right: 8px solid var(--tooltip-bg); border-top: 8px solid transparent; border-bottom: 8px solid transparent; left: -7px; @@ -369,7 +369,7 @@ .tippy-tooltip { position: relative; color: #fff; - color: var(--body-bg); + color: var(--tooltip-color); border-radius: 4px; font-size: 0.9rem; padding: 0.3rem 0.6rem; @@ -379,7 +379,7 @@ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; background-color: #333; - background-color: var(--gray); + background-color: var(--tooltip-bg); } .tippy-tooltip[data-size='small'] { @@ -428,7 +428,7 @@ position: absolute; will-change: transform; background-color: #333; - background-color: var(--gray); + background-color: var(--tooltip-bg); border-radius: 50%; width: calc(110% + 2rem); left: 50%; diff --git a/website/src/styles/utils/css-variables.scss b/website/src/styles/utils/css-variables.scss index b1c657297a..79bac2b4df 100644 --- a/website/src/styles/utils/css-variables.scss +++ b/website/src/styles/utils/css-variables.scss @@ -25,6 +25,10 @@ // Body font color --body-color: #{$body-color}; + // Tooltips (Tippy) + --tooltip-bg: #{$gray-dark}; + --tooltip-color: #fff; + // Shade of grays --gray: #{$gray}; --gray-mid: #{$gray-mid}; @@ -57,6 +61,10 @@ body.mode-dark { // Body font color --body-color: #aaa; + // Tooltips (Tippy) + --tooltip-bg: #3a3a3a; + --tooltip-color: var(--body-color); + // Shade of grays --gray: #aaa; --gray-mid: #7d7d7d; diff --git a/website/src/views/components/module-info/RequisiteRulePopup.scss b/website/src/views/components/module-info/RequisiteRulePopup.scss new file mode 100644 index 0000000000..c8b685a383 --- /dev/null +++ b/website/src/views/components/module-info/RequisiteRulePopup.scss @@ -0,0 +1,51 @@ +@import '~styles/utils/modules-entry'; + +.tooltip { + padding: 0.75rem 1rem; +} + +.trigger { + display: inline-flex; + flex-shrink: 0; + justify-content: center; + align-items: center; + padding: 0.15rem; + border: 0; + line-height: 0; + color: var(--gray-light); + background: transparent; + cursor: pointer; + + &:hover { + color: theme-color(); + } + + &:focus:not(:focus-visible) { + outline: none; + } + + &:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; + color: theme-color(); + } + + :global(.svg) { + display: block; + width: 1.1rem; + height: 1.1rem; + } +} + +.content { + max-width: 28rem; + font-size: $font-size-sm; + text-align: left; + white-space: pre-wrap; + word-break: break-word; + color: var(--tooltip-color); + + :global(a) { + color: theme-color(); + } +} diff --git a/website/src/views/components/module-info/RequisiteRulePopup.test.tsx b/website/src/views/components/module-info/RequisiteRulePopup.test.tsx new file mode 100644 index 0000000000..a347059a34 --- /dev/null +++ b/website/src/views/components/module-info/RequisiteRulePopup.test.tsx @@ -0,0 +1,36 @@ +import { screen } from '@testing-library/react'; +import { Provider } from 'react-redux'; + +import { initAction } from 'test-utils/redux'; +import reducers from 'reducers'; +import configureStore from 'bootstrapping/configure-store'; +import renderWithRouterMatch from 'test-utils/renderWithRouterMatch'; +import RequisiteRulePopup from './RequisiteRulePopup'; + +describe(RequisiteRulePopup, () => { + function make(rule: string, summary?: string) { + const initialState = reducers(undefined, initAction()); + const { store } = configureStore(initialState); + + return renderWithRouterMatch( + + + , + {}, + ); + } + + it('renders nothing when rule matches summary', () => { + const rule = 'CS1010 or its equivalent'; + const { view } = make(rule, rule); + expect(view.container).toBeEmptyDOMElement(); + }); + + it('renders info icon with accessible label', () => { + make('PROGRAM_TYPES IF_IN Undergraduate Degree THEN (COURSES (1) CS1010:D)', 'CS1010'); + + expect( + screen.getByRole('button', { name: 'View detailed prerequisite rule' }), + ).toBeInTheDocument(); + }); +}); diff --git a/website/src/views/components/module-info/RequisiteRulePopup.tsx b/website/src/views/components/module-info/RequisiteRulePopup.tsx new file mode 100644 index 0000000000..5f294c08e1 --- /dev/null +++ b/website/src/views/components/module-info/RequisiteRulePopup.tsx @@ -0,0 +1,45 @@ +import { Info } from 'react-feather'; + +import LinkModuleCodes from 'views/components/LinkModuleCodes'; +import Tooltip from 'views/components/Tooltip'; +import styles from './RequisiteRulePopup.scss'; + +type Props = { + rule: string; + summary?: string; + label?: string; +}; + +/** + * Info icon that shows the full structured requisite rule in a popup. + */ +export default function RequisiteRulePopup({ + rule, + summary, + label = 'View detailed prerequisite rule', +}: Props) { + if (!rule.trim()) return null; + if (summary?.trim() === rule.trim()) return null; + + const content = ( +
+ {rule} +
+ ); + + return ( + + + + ); +} diff --git a/website/src/views/modules/ModulePageContent.scss b/website/src/views/modules/ModulePageContent.scss index fc744633bc..fe0c16b626 100644 --- a/website/src/views/modules/ModulePageContent.scss +++ b/website/src/views/modules/ModulePageContent.scss @@ -59,6 +59,22 @@ $sticky-top: $navbar-height; font-size: $font-size-lg; } +.sectionHeadingRow { + display: flex; + align-items: center; + padding: 0.6rem 0; + margin: 0 0 0.6rem; + border-bottom: 1px solid var(--gray-lighter); + gap: 0.4rem; + + .sectionHeading { + flex: 1 1 auto; + padding: 0; + margin: 0; + border-bottom: none; + } +} + .descriptionHeading { margin-bottom: 0; font-weight: bold; diff --git a/website/src/views/modules/ModulePageContent.tsx b/website/src/views/modules/ModulePageContent.tsx index fe842a3cb4..c36666f88d 100644 --- a/website/src/views/modules/ModulePageContent.tsx +++ b/website/src/views/modules/ModulePageContent.tsx @@ -22,6 +22,7 @@ import LessonTimetable from 'views/components/module-info/LessonTimetable'; import ModuleExamClash from 'views/components/module-info/ModuleExamClash'; import ModuleWorkload from 'views/components/module-info/ModuleWorkload'; import ModuleExamInfo from 'views/components/module-info/ModuleExamInfo'; +import RequisiteRulePopup from 'views/components/module-info/RequisiteRulePopup'; import AddModuleDropdown from 'views/components/module-info/AddModuleDropdown'; import SaveModuleButton from 'views/components/module-info/SaveModuleButton'; import Announcements from 'views/components/notfications/Announcements'; @@ -266,7 +267,12 @@ const ModulePageContent: React.FC = ({ module, archiveYear }) => {
-

Prerequisite Tree

+
+

Prerequisite Tree

+ {module.prerequisiteRule && ( + + )} +
= (props) => {

This new version of the prerequisite tree is being tested and may not be accurate. Viewers - are encouraged to double check details with the prerequisite text above. To report bugs with - the new tree, please post a bug report on GitHub (preferred) at{' '} + are encouraged to double check details with the prerequisite information above. To report + bugs with the new tree, please post a bug report on GitHub (preferred) at{' '} should grey out modules that are not in module ba

- This new version of the prerequisite tree is being tested and may not be accurate. Viewers are encouraged to double check details with the prerequisite text above. To report bugs with the new tree, please post a bug report on GitHub (preferred) at + This new version of the prerequisite tree is being tested and may not be accurate. Viewers are encouraged to double check details with the prerequisite information above. To report bugs with the new tree, please post a bug report on GitHub (preferred) at should render prereq branch with nOf condition >

- This new version of the prerequisite tree is being tested and may not be accurate. Viewers are encouraged to double check details with the prerequisite text above. To report bugs with the new tree, please post a bug report on GitHub (preferred) at + This new version of the prerequisite tree is being tested and may not be accurate. Viewers are encouraged to double check details with the prerequisite information above. To report bugs with the new tree, please post a bug report on GitHub (preferred) at should render prereq tree of module > CS3244 1`]

- This new version of the prerequisite tree is being tested and may not be accurate. Viewers are encouraged to double check details with the prerequisite text above. To report bugs with the new tree, please post a bug report on GitHub (preferred) at + This new version of the prerequisite tree is being tested and may not be accurate. Viewers are encouraged to double check details with the prerequisite information above. To report bugs with the new tree, please post a bug report on GitHub (preferred) at should render prereq tree to the right when tree

- This new version of the prerequisite tree is being tested and may not be accurate. Viewers are encouraged to double check details with the prerequisite text above. To report bugs with the new tree, please post a bug report on GitHub (preferred) at + This new version of the prerequisite tree is being tested and may not be accurate. Viewers are encouraged to double check details with the prerequisite information above. To report bugs with the new tree, please post a bug report on GitHub (preferred) at should render requirements fulfilled tree of modu

- This new version of the prerequisite tree is being tested and may not be accurate. Viewers are encouraged to double check details with the prerequisite text above. To report bugs with the new tree, please post a bug report on GitHub (preferred) at + This new version of the prerequisite tree is being tested and may not be accurate. Viewers are encouraged to double check details with the prerequisite information above. To report bugs with the new tree, please post a bug report on GitHub (preferred) at