diff --git a/examples/district/app/[locale]/[district]/[department]/[scheme]/components/Explorer.tsx b/examples/district/app/[locale]/[district]/[department]/[scheme]/components/Explorer.tsx index 9100f670..136abf43 100644 --- a/examples/district/app/[locale]/[district]/[department]/[scheme]/components/Explorer.tsx +++ b/examples/district/app/[locale]/[district]/[department]/[scheme]/components/Explorer.tsx @@ -5,14 +5,15 @@ import { useFetch } from '@/lib/api'; import { cn, copyURLToClipboard, exportAsImage } from '@/lib/utils'; import { Button, - Combobox, ComboboxMulti, Select, + SelectorCard, Tab, TabList, TabPanel, Tabs, Text, + Tray, useToast, } from 'opub-ui'; import React from 'react'; @@ -20,6 +21,7 @@ import { ErrorBoundary } from 'react-error-boundary'; import { useQueryState } from 'next-usequerystate'; import dynamic from 'next/dynamic'; import { BarView } from './BarView'; +import { useWindowSize } from '@/hooks/use-window-size'; const LeafletChoropleth = dynamic( () => import('opub-viz').then((mod) => mod.LeafletChoropleth), @@ -42,6 +44,8 @@ export const Explorer = React.forwardRef( const [selectedYear, setYear] = React.useState(Object.keys(years)[0]); const [selectedTab, setTab] = React.useState<'map' | 'bar'>('bar'); const [indicator, setIndicator] = useQueryState('indicator'); + const [indicatorName, setIndicatorName] = React.useState('indicator'); + const [trayOpen, setTrayOpen] = React.useState(false); const { data: indicatorData, isLoading } = useFetch( 'indicators', @@ -58,10 +62,28 @@ export const Explorer = React.forwardRef( } }, [indicatorData, indicator]); + // TODO: improve this section by adding better API + // set indicator name + React.useEffect(() => { + if (indicatorData) { + const indicatorObjsArr: any = Object.values( + indicatorData[scheme as string] + ); + indicatorObjsArr.forEach((objsArr: any) => { + const indicatorObj = objsArr.find((e: any) => e.slug === indicator); + + if (indicatorObj) { + setIndicatorName(indicatorObj.label); + return; + } + }); + } + }, [indicatorData, indicator]); + return (
@@ -82,27 +104,35 @@ export const Explorer = React.forwardRef(
)}
- {/*
- ({ label: year, value: year, }))} + className="w-full md:w-fit" />
{ fontWeight="medium" color="subdued" as="p" - className="mt-4" + className="mt-3 md:mt-4" > Last Updated: {data.schemeData.lastUpdated} @@ -198,11 +199,18 @@ const TabLayout = ({ 'tab', parseAsString.withDefault('overview') ); - const { toast } = useToast(); const overviewRef: any = React.useRef(null); + const { toast } = useToast(); + const { width } = useWindowSize(); + const isMobile = width && width < 768; + return ( - + ))} -
+
{tabValue === 'overview' && (
); @@ -116,7 +116,7 @@ export const ContentCard = ({ View on Explorer - + )}
@@ -166,7 +166,7 @@ export const ProgressCard = ({ View on Explorer - + )}
diff --git a/examples/district/app/[locale]/[district]/[department]/components/Content.module.scss b/examples/district/app/[locale]/[district]/[department]/components/Content.module.scss index 8422b760..cb695548 100644 --- a/examples/district/app/[locale]/[district]/[department]/components/Content.module.scss +++ b/examples/district/app/[locale]/[district]/[department]/components/Content.module.scss @@ -20,4 +20,9 @@ transform: rotate(-180deg); } } + + @media (max-width: 768px) { + padding: var(--space-3, 12px) var(--space-3, 12px) + var(--border-radius-4, 16px) var(--space-3, 12px); + } } diff --git a/examples/district/app/[locale]/[district]/[department]/components/department-layout.tsx b/examples/district/app/[locale]/[district]/[department]/components/department-layout.tsx index a2c4d4a3..5fd78eee 100644 --- a/examples/district/app/[locale]/[district]/[department]/components/department-layout.tsx +++ b/examples/district/app/[locale]/[district]/[department]/components/department-layout.tsx @@ -71,7 +71,7 @@ export function Content({ data }: { data: IProps }) { - +
{departmentData.collapsible.content.map((item) => ( diff --git a/examples/district/app/[locale]/[district]/components/Card.tsx b/examples/district/app/[locale]/[district]/components/Card.tsx index a8ad48a7..34128f0b 100644 --- a/examples/district/app/[locale]/[district]/components/Card.tsx +++ b/examples/district/app/[locale]/[district]/components/Card.tsx @@ -46,7 +46,7 @@ export const DepartmentCard = ({ Explore More about {data.label} - +
diff --git a/examples/district/app/[locale]/[district]/components/Content.module.scss b/examples/district/app/[locale]/[district]/components/Content.module.scss index cd30ce7c..c23d1a10 100644 --- a/examples/district/app/[locale]/[district]/components/Content.module.scss +++ b/examples/district/app/[locale]/[district]/components/Content.module.scss @@ -20,6 +20,11 @@ transform: rotate(-180deg); } } + + @media (max-width: 768px) { + padding: var(--space-3, 12px) var(--space-3, 12px) + var(--border-radius-4, 16px) var(--space-3, 12px); + } } .Main { diff --git a/examples/district/app/[locale]/[district]/components/dashboard-layout.tsx b/examples/district/app/[locale]/[district]/components/dashboard-layout.tsx index 1766d315..19fe630a 100644 --- a/examples/district/app/[locale]/[district]/components/dashboard-layout.tsx +++ b/examples/district/app/[locale]/[district]/components/dashboard-layout.tsx @@ -3,7 +3,6 @@ import { IconButton } from 'opub-ui'; import styles from './Content.module.scss'; import { DashboardSidebar } from './dashboard-sidebar'; -import { MobileDashboardNav } from './mobile-dashboard-nav'; import { cn } from '@/lib/utils'; import React from 'react'; import Icons from '@/components/icons'; diff --git a/examples/district/app/[locale]/[district]/components/district-layout.tsx b/examples/district/app/[locale]/[district]/components/district-layout.tsx index 003f6fb3..540eeaf2 100644 --- a/examples/district/app/[locale]/[district]/components/district-layout.tsx +++ b/examples/district/app/[locale]/[district]/components/district-layout.tsx @@ -64,7 +64,7 @@ export function Content({ data }: { data: IProps }) { - +
@@ -83,8 +83,8 @@ export function Content({ data }: { data: IProps }) { ))}
-
-
+
+
About {data.title} diff --git a/examples/district/app/[locale]/components/district-selector-mobile.tsx b/examples/district/app/[locale]/components/district-selector-mobile.tsx new file mode 100644 index 00000000..a1defe73 --- /dev/null +++ b/examples/district/app/[locale]/components/district-selector-mobile.tsx @@ -0,0 +1,126 @@ +import { Button, Divider, Icon, SearchInput, Text, Tray } from 'opub-ui'; +import { + assamDistrictCategory, + availableDistricts, + filterDistricts, +} from '../home.config'; +import { cn } from '@/lib/utils'; +import Link from 'next/link'; +import Icons from '@/components/icons'; +import React from 'react'; + +export const DistrictSelectorMobile = () => { + const [search, setSearch] = React.useState(''); + const [districtList, setDistrictList] = React.useState(assamDistrictCategory); + + // filter districtList based on search + React.useEffect(() => { + if (search) { + const filteredDistricts = filterDistricts(search.toLowerCase()); + setDistrictList(filteredDistricts); + } else { + setDistrictList(assamDistrictCategory); + } + }, [search]); + + return ( +
+
+ + Districts + + + Select district to view insights + + +
+
+ {availableDistricts.map((district) => ( + + + + {district.name} + + + ))} +
+ + View All Districts + + } + size="extended" + > +
+ + Districts + + + Select district to view insights + + +
+ {availableDistricts.map((district) => ( + + + + {district.name} + + + ))} +
+ + + +
+ {Object.values(districtList).map((category) => ( +
+
+ + {category.name} + +
+ +
+ {category.districts.map((district) => ( + + {district} + + ))} +
+
+ ))} +
+
+
+
+ ); +}; diff --git a/examples/district/app/[locale]/components/district-selector.tsx b/examples/district/app/[locale]/components/district-selector.tsx index da092456..cdd54317 100644 --- a/examples/district/app/[locale]/components/district-selector.tsx +++ b/examples/district/app/[locale]/components/district-selector.tsx @@ -59,8 +59,8 @@ export const DistrictSelector = () => { }; return ( -
-
+
+
{!mapLoading && ( { return ( -
+
Quick Links -
+
{content.map((item, index) => ( { > {item.district} - {/* TODO: change opub-ui variables to new one */}
diff --git a/examples/district/app/[locale]/page.tsx b/examples/district/app/[locale]/page.tsx index 7d8881d9..95dc3800 100644 --- a/examples/district/app/[locale]/page.tsx +++ b/examples/district/app/[locale]/page.tsx @@ -3,6 +3,7 @@ import { Header, KeyHighlights, QuickLinks, + DistrictSelectorMobile } from './components'; export default function Home() { @@ -14,6 +15,7 @@ export default function Home() {
+
diff --git a/examples/district/components/icons.tsx b/examples/district/components/icons.tsx index b9a0afa3..f65963ca 100644 --- a/examples/district/components/icons.tsx +++ b/examples/district/components/icons.tsx @@ -34,6 +34,7 @@ import { IconX, TablerIconsProps, IconChevronsRight, + IconCircleChevronRight, } from '@tabler/icons-react'; export const Icons: { @@ -48,7 +49,7 @@ export const Icons: { arrowRight: IconArrowRight, up: IconChevronUp, down: IconChevronDown, - right: IconChevronRight, + right: IconCircleChevronRight, left: IconChevronLeft, doubleLeft: IconChevronsLeft, doubleRight: IconChevronsRight, diff --git a/packages/opub-ui/src/components/Pill/Pill.module.scss b/packages/opub-ui/src/components/Pill/Pill.module.scss index 54b5edc0..52a1de11 100644 --- a/packages/opub-ui/src/components/Pill/Pill.module.scss +++ b/packages/opub-ui/src/components/Pill/Pill.module.scss @@ -4,6 +4,7 @@ $height: 20px; .Pill { display: inline-flex; + justify-content: space-between; align-items: center; max-width: 100%; padding: var(--space-1, 4px) var(--space-2, 8px) var(--space-1, 4px) @@ -21,10 +22,6 @@ $height: 20px; pointer-events: none; } - &.linkable { - padding: 0; - } - &.variantInfo { background-color: var(--surface-highlight-subdued); border-color: var(--border-highlight-default); @@ -70,35 +67,3 @@ $height: 20px; pointer-events: none; } } - -.Link { - display: inline-grid; - color: var(--text-default); - outline: none; - border-radius: var(--border-radius-1); - text-decoration: none; - min-height: $height; - padding: 0 var(--space-2); - - @include focus-ring; - - &:focus-visible:not(:active) { - @include focus-ring($style: 'focused'); - text-decoration: underline; - } - - &:hover { - background: var(--surface-highlight-hovered); - text-decoration: underline; - } - - &.segmented { - &:hover { - background: none; - } - - &::after { - margin-right: var(--space-1); - } - } -} diff --git a/packages/opub-ui/src/components/Pill/Pill.tsx b/packages/opub-ui/src/components/Pill/Pill.tsx index 3991bac2..ad6bb950 100644 --- a/packages/opub-ui/src/components/Pill/Pill.tsx +++ b/packages/opub-ui/src/components/Pill/Pill.tsx @@ -15,9 +15,9 @@ export const Pill = React.forwardRef( disabled = false, onRemove = () => {}, accessibilityLabel, - url, returnValue = '', variant = 'neutral', + truncate, ...other }: PillProps, ref: React.LegacyRef @@ -26,8 +26,7 @@ export const Pill = React.forwardRef( styles.Pill, styles.removable, variant && styles[variationName('variant', variant)], - disabled && styles.disabled, - url && !disabled && styles.linkable + disabled && styles.disabled ); let tagTitle = accessibilityLabel; @@ -51,14 +50,11 @@ export const Pill = React.forwardRef( ); - const tagContent = - url && !disabled ? ( - - {children} - - ) : ( - {children} - ); + const tagContent = ( + + {children} + + ); return ( diff --git a/packages/opub-ui/src/components/SearchInput/SearchInput.tsx b/packages/opub-ui/src/components/SearchInput/SearchInput.tsx index 46acfb12..40ac693d 100644 --- a/packages/opub-ui/src/components/SearchInput/SearchInput.tsx +++ b/packages/opub-ui/src/components/SearchInput/SearchInput.tsx @@ -30,7 +30,7 @@ type Props = { const SearchInput = forwardRef((props: Props, ref: any) => { const [search, setSearch] = React.useState(props.defaultValue || ''); - const className = cn(styles.SearchInput, props.className); + const className = cn(props.withButton && styles.SearchInput, props.className); return (
span { - font-size: var(--font-size-200); - line-height: var(--font-line-height-2); - } - } } .SelectedOption { diff --git a/packages/opub-ui/src/components/Select/Select.tsx b/packages/opub-ui/src/components/Select/Select.tsx index 4b78d0c8..1862d79a 100644 --- a/packages/opub-ui/src/components/Select/Select.tsx +++ b/packages/opub-ui/src/components/Select/Select.tsx @@ -108,7 +108,9 @@ export const Select = forwardRef(
{inlineLabelMarkup} {prefixMarkup} - {selectedOption.label} + + {selectedOption.label} + diff --git a/packages/opub-ui/src/components/SelectorCard/SelectorCard.module.scss b/packages/opub-ui/src/components/SelectorCard/SelectorCard.module.scss new file mode 100644 index 00000000..43f7fb57 --- /dev/null +++ b/packages/opub-ui/src/components/SelectorCard/SelectorCard.module.scss @@ -0,0 +1,19 @@ +@import '../../../styles/common'; + +.SelectorCard { + display: flex; + flex-direction: column; + gap: var(--space-2); + padding: var(--space-3, 12px); + max-width: 480px; + width: 100%; + border-radius: var(--border-radius-05, 2px); + background: var(--surface-default, #fff); + box-shadow: var(--shadow-element-card); +} + +.PillWrapper { + display: flex; + flex-direction: column; + gap: var(--space-2); +} diff --git a/packages/opub-ui/src/components/SelectorCard/SelectorCard.stories.tsx b/packages/opub-ui/src/components/SelectorCard/SelectorCard.stories.tsx new file mode 100644 index 00000000..54d189c6 --- /dev/null +++ b/packages/opub-ui/src/components/SelectorCard/SelectorCard.stories.tsx @@ -0,0 +1,54 @@ +import { Pill } from '../Pill'; +import { SelectorCard } from './SelectorCard'; +import { Meta, StoryObj } from '@storybook/react'; + +/** + * Selector Card is a component that displays a title, a selected value, and a button. + */ +const meta = { + component: SelectorCard, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + title: 'SelectorCard', + selected: + 'Person Days Generated as a share of Cumulative Projection of Person Days', + buttonText: 'Switch Indicator', + onClick: () => {}, + }, +}; + +export const Pills: Story = { + args: { + title: 'SelectorCard', + selected: ( + <> + { + console.log(e); + }} + returnValue="1" + > + Person Days Generated as a share of Cumulative Projection of Person + Days + + {}} returnValue="2"> + Person Days Generated as a share of Cumulative Projection of Person + Days + + {}} returnValue="3"> + Person Days Generated as a share of Cumulative Projection of Person + Days + + + ), + buttonText: 'Edit Indicators', + onClick: () => {}, + }, +}; diff --git a/packages/opub-ui/src/components/SelectorCard/SelectorCard.test.tsx b/packages/opub-ui/src/components/SelectorCard/SelectorCard.test.tsx new file mode 100644 index 00000000..caceeedb --- /dev/null +++ b/packages/opub-ui/src/components/SelectorCard/SelectorCard.test.tsx @@ -0,0 +1,13 @@ +import "@testing-library/jest-dom"; +import { render, screen } from "@testing-library/react"; +import { SelectorCard } from "./SelectorCard"; + +describe("SelectorCard Tests", () => { + beforeEach(() => { + render(Component); + }); + + test("should show Component text all the time", () => { + expect(screen.getByText(/Component/i)).toBeInTheDocument(); + }); +}); diff --git a/packages/opub-ui/src/components/SelectorCard/SelectorCard.tsx b/packages/opub-ui/src/components/SelectorCard/SelectorCard.tsx new file mode 100644 index 00000000..b0095c84 --- /dev/null +++ b/packages/opub-ui/src/components/SelectorCard/SelectorCard.tsx @@ -0,0 +1,42 @@ +import { cn } from '../../utils'; +import { Button } from '../Button'; +import { Divider } from '../Divider'; +import { Text } from '../Text'; +import styles from './SelectorCard.module.scss'; +import React, { forwardRef } from 'react'; + +type Props = { + title: string; + selected: string | React.ReactNode; + buttonText: string; + onClick: () => void; +}; + +const SelectorCard = forwardRef((props: Props, ref: any) => { + const { title, selected, buttonText, onClick } = props; + const themeClass = cn(styles.SelectorCard); + + let content; + if (typeof selected === 'string') { + content = ( + + {selected} + + ); + } else { + content =
{selected}
; + } + + return ( +
+ {title} + {content} + + +
+ ); +}); + +export { SelectorCard }; diff --git a/packages/opub-ui/src/components/SelectorCard/index.ts b/packages/opub-ui/src/components/SelectorCard/index.ts new file mode 100644 index 00000000..7e0fda19 --- /dev/null +++ b/packages/opub-ui/src/components/SelectorCard/index.ts @@ -0,0 +1 @@ +export { SelectorCard } from "./SelectorCard"; diff --git a/packages/opub-ui/src/components/Tabs/Tabs.module.scss b/packages/opub-ui/src/components/Tabs/Tabs.module.scss index 35c373b0..03626f08 100644 --- a/packages/opub-ui/src/components/Tabs/Tabs.module.scss +++ b/packages/opub-ui/src/components/Tabs/Tabs.module.scss @@ -31,6 +31,12 @@ $item-min-width: 50px; cursor: pointer; box-shadow: var(--shadow-inset-basic); + @media #{$breakpoints-md-down} { + padding-block: var(--space-2); + padding-inline-start: var(--space-5); + padding-inline-end: var(--space-6); + } + &::before { content: ''; position: absolute; @@ -64,7 +70,7 @@ $item-min-width: 50px; } &[data-state='active'] { - background: var(--surface-highlight-subdued, #fbfaff); + background: var(--surface-highlight-default, #fbfaff); .Title { color: var(--text-highlight); diff --git a/packages/opub-ui/src/components/Text/Text.module.scss b/packages/opub-ui/src/components/Text/Text.module.scss index f7b93283..96bbe3f4 100644 --- a/packages/opub-ui/src/components/Text/Text.module.scss +++ b/packages/opub-ui/src/components/Text/Text.module.scss @@ -36,14 +36,14 @@ text-align: justify; } -.default { - color: var(--text-default); -} - .inherit { color: inherit; } +.default { + color: var(--text-default); +} + .disabled { color: var(--text-disabled); } @@ -64,10 +64,22 @@ color: var(--text-subdued); } -.text-inverse { +.highlight { + color: var(--text-highlight); +} + +.interactive { + color: var(--text-interactive); +} + +.onBgDefault { color: var(--text-onbg-default); } +.onBgDisabled { + color: var(--text-onbg-disabled); +} + .regular { font-weight: 400; } diff --git a/packages/opub-ui/src/components/Text/Text.stories.tsx b/packages/opub-ui/src/components/Text/Text.stories.tsx index a3c1fadf..9ebcd904 100644 --- a/packages/opub-ui/src/components/Text/Text.stories.tsx +++ b/packages/opub-ui/src/components/Text/Text.stories.tsx @@ -40,6 +40,9 @@ export const Variants = () => ( Text with HeadingXs variant + + Text with headingSmSpaced variant + Text with BodyLg variant @@ -91,30 +94,53 @@ export const WithFontWeight = () => ( ); export const WithColor = () => ( - + + + Default Text is used to communicate the majority of information on the + - Use to de-emphasize a piece of text that is less important to merchants - than other nearby text. May also be used to indicate when normal content - is absent, for example, “No supplier listed”. Don’t use only for aesthetic - effect. + Subdued Text is used to create a hierarchy of information on the page. + + + Disabled Text is used to indicate that a field is disabled and cannot be + interacted with. - Use in combination with a symbol showing an increasing value to indicate - an upward trend. + Use to indicate that something was successful. - Use to denote something that needs attention, or that merchants need to - take action on. + Use to indicate that something needs attention. - Use in combination with a symbol showing a decreasing value to indicate a - downward trend. - -
- + Use to indicate that something has failed. + + + Highlight Text is used to highlight important information on the page. + + + Interactive Text is used to indicate that something can be interacted + with. + +
+ Use in situations where background is dark.
+
+ + This is the disabled version for dark background + +
); diff --git a/packages/opub-ui/src/components/index.ts b/packages/opub-ui/src/components/index.ts index 8e904238..af201bc9 100644 --- a/packages/opub-ui/src/components/index.ts +++ b/packages/opub-ui/src/components/index.ts @@ -58,5 +58,7 @@ export { TextField } from './TextField'; export { Thumbnail } from './Thumbnail'; export { Toast, Toaster, useToast } from './Toast'; export { Tooltip } from './Tooltip'; -export { SearchInput } from "./SearchInput"; -export { Tray } from "./Tray"; +export { SearchInput } from './SearchInput'; +export { Tray } from './Tray'; +export { SelectorCard } from './SelectorCard'; +export { Pill } from './Pill'; diff --git a/packages/opub-ui/src/types/pill.ts b/packages/opub-ui/src/types/pill.ts index 453030a9..8d9b9365 100644 --- a/packages/opub-ui/src/types/pill.ts +++ b/packages/opub-ui/src/types/pill.ts @@ -1,4 +1,4 @@ -interface NonMutuallyExclusiveProps { +export type PillProps = { /** Content to display in the tag */ children?: React.ReactNode; /** Disables the tag */ @@ -11,10 +11,6 @@ interface NonMutuallyExclusiveProps { variant?: 'neutral' | 'info' | 'success' | 'warning' | 'critical'; /** Value that is returned on click of remove button */ returnValue?: string; -} - -export type PillProps = NonMutuallyExclusiveProps & - ( - | { onClick?(): void; onRemove?: undefined; url?: undefined } - | { onClick?: undefined; onRemove?(value: string): void; url?: string } - ); + /** Truncate text to one line */ + truncate?: boolean; +}; diff --git a/packages/opub-ui/src/types/text.ts b/packages/opub-ui/src/types/text.ts index 914c4a83..a3ac9b76 100644 --- a/packages/opub-ui/src/types/text.ts +++ b/packages/opub-ui/src/types/text.ts @@ -30,13 +30,17 @@ type Alignment = 'start' | 'center' | 'end' | 'justify'; type FontWeight = 'regular' | 'medium' | 'semibold' | 'bold'; type Color = - | 'success' - | 'critical' - | 'warning' + | 'default' + | 'medium' | 'subdued' - | 'text-inverse' | 'disabled' - | 'default' + | 'critical' + | 'warning' + | 'success' + | 'highlight' + | 'interactive' + | 'onBgDefault' + | 'onBgDisabled' | 'inherit'; export const VariantFontWeightMapping: { [V in Variant]: FontWeight } = {