Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(frontend): Mantine Component Library PoC #5344

Merged
merged 19 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3105f47
refactor(frontend): add Mantine Component Library
Akuukis Dec 11, 2024
453f2c1
wip(frontend): example mantine usage
Akuukis Dec 11, 2024
15b3557
refactor(theme): rename theme
Akuukis Dec 12, 2024
d782e38
refactor(theme): style Button
Akuukis Dec 12, 2024
eca5c85
wip: TODO before merge
Akuukis Dec 12, 2024
cde2874
refactor(theme): extend Button with tooltip
Akuukis Dec 12, 2024
7142d55
Merge branch 'main' into kalvis/mantine-setup
magicznyleszek Dec 16, 2024
511b407
Fix Checkbox component misaligned element caused by Mantine's CSS Reset
magicznyleszek Dec 16, 2024
27e8f95
setup Storybook for Mantine by following official guide
magicznyleszek Dec 18, 2024
2df7caa
improve gray color comments from Mantine theme
magicznyleszek Dec 18, 2024
717f080
Merge branch 'main' into kalvis/mantine-setup
magicznyleszek Dec 18, 2024
b5695da
Merge branch 'main' into kalvis/mantine-setup
magicznyleszek Dec 19, 2024
af2517c
Merge branch 'main' of github.com:kobotoolbox/kpi into kalvis/mantine…
magicznyleszek Dec 30, 2024
29c9092
Merge branch 'kalvis/mantine-setup' of github.com:kobotoolbox/kpi int…
magicznyleszek Dec 30, 2024
9293760
Merge branch 'main' into kalvis/mantine-setup
magicznyleszek Jan 4, 2025
d4dea03
refactor(SimpleTable): drop BEM, use Mantine TASK-1377 (#5366)
magicznyleszek Jan 6, 2025
bafb0be
Merge branch 'main' into kalvis/mantine-setup
magicznyleszek Jan 6, 2025
3e62d24
feat(organizations): add mantine menu theme/story TASK-1382 (#5410)
jamesrkiger Jan 8, 2025
4691dde
Remove todo comment
jamesrkiger Jan 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ if (process.env.MEASURE) {
}
module.exports = {
stories: ['../jsapp/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions', '@storybook/addon-a11y'
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-a11y',
'storybook-dark-mode',
// NB:
// 'storybook-addon-swc' may improve build speed in the future.
// - At time of writing, the build performance gains are negated because it
Expand Down Expand Up @@ -95,4 +100,4 @@ function applySpeedTweaks(config) {
terserOptions: {}
})];
}
}
}
32 changes: 32 additions & 0 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,37 @@
import 'jsapp/scss/main.scss';
import 'js/bemComponents';
import '@mantine/core/styles.css';
import {useEffect} from 'react';
import {addons} from '@storybook/preview-api';
import {DARK_MODE_EVENT_NAME} from 'storybook-dark-mode';
import {
MantineProvider,
useMantineColorScheme,
} from '@mantine/core';
import {themeKobo} from 'jsapp/js/theme';

const channel = addons.getChannel();

function ColorSchemeWrapper({children}) {
const {setColorScheme} = useMantineColorScheme();
const handleColorScheme = (value) => setColorScheme(value ? 'dark' : 'light');

useEffect(() => {
channel.on(DARK_MODE_EVENT_NAME, handleColorScheme);
return () => channel.off(DARK_MODE_EVENT_NAME, handleColorScheme);
}, [channel]);

return <>{children}</>;
}

export const decorators = [
(renderStory) => (
<ColorSchemeWrapper>{renderStory()}</ColorSchemeWrapper>
),
(renderStory) => (
<MantineProvider theme={themeKobo}>{renderStory()}</MantineProvider>
),
];

export const parameters = {
options: {
Expand Down
75 changes: 40 additions & 35 deletions jsapp/js/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ import {
import {isAnyProcessingRouteActive} from 'js/components/processing/routes.utils';
import pageState from 'js/pageState.store';

import '@mantine/core/styles.css';
import { MantineProvider } from '@mantine/core';

// Query-related
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { queryClient } from './query/queryClient.ts';
import { RequireOrg } from './router/RequireOrg';
import { themeKobo } from './theme';

class App extends React.Component {
constructor(props) {
Expand Down Expand Up @@ -107,46 +110,48 @@ class App extends React.Component {
return (
<DocumentTitle title='KoboToolbox'>
<QueryClientProvider client={queryClient}>
<RootContextProvider>
<RequireOrg>
<Tracking />
<ToasterConfig />

{this.shouldDisplayMainLayoutElements() &&
<div className='header-stretch-bg' />
}

<bem.PageWrapper
m={pageWrapperModifiers}
className='mdl-layout mdl-layout--fixed-header'
>
{this.state.pageState.modal && (
<BigModal params={this.state.pageState.modal} />
)}

{this.shouldDisplayMainLayoutElements() && (
<>
<MainHeader assetUid={assetid} />
<Drawer />
</>
)}

<bem.PageWrapper__content
className='mdl-layout__content'
m={pageWrapperContentModifiers}
<MantineProvider theme={themeKobo}>
<RootContextProvider>
<RequireOrg>
<Tracking />
<ToasterConfig />

{this.shouldDisplayMainLayoutElements() &&
<div className='header-stretch-bg' />
}

<bem.PageWrapper
m={pageWrapperModifiers}
className='mdl-layout mdl-layout--fixed-header'
>
{this.state.pageState.modal && (
<BigModal params={this.state.pageState.modal} />
)}

{this.shouldDisplayMainLayoutElements() && (
<>
{this.isFormSingle() && <ProjectTopTabs />}
<FormViewSideTabs show={this.isFormSingle()} />
<MainHeader assetUid={assetid} />
<Drawer />
</>
)}

<Outlet />
</bem.PageWrapper__content>
</bem.PageWrapper>
</RequireOrg>
</RootContextProvider>
<bem.PageWrapper__content
className='mdl-layout__content'
m={pageWrapperContentModifiers}
>
{this.shouldDisplayMainLayoutElements() && (
<>
{this.isFormSingle() && <ProjectTopTabs />}
<FormViewSideTabs show={this.isFormSingle()} />
</>
)}

<Outlet />
</bem.PageWrapper__content>
</bem.PageWrapper>
</RequireOrg>
</RootContextProvider>
</MantineProvider>


{/* React Query Devtools - GUI for inspecting and modifying query status
Expand Down
9 changes: 0 additions & 9 deletions jsapp/js/bemComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,15 +179,6 @@ bem.KDrawer__primaryIcons = makeBem(bem.KDrawer, 'primary-icons', 'nav');
bem.KDrawer__secondaryIcons = makeBem(bem.KDrawer, 'secondary-icons', 'nav');
bem.KDrawer__sidebar = makeBem(bem.KDrawer, 'sidebar', 'aside');

bem.SimpleTable = makeBem(null, 'simple-table', 'table');
bem.SimpleTable__header = makeBem(bem.SimpleTable, 'header', 'thead');
bem.SimpleTable__body = makeBem(bem.SimpleTable, 'body', 'tbody');
bem.SimpleTable__footer = makeBem(bem.SimpleTable, 'footer', 'tfoot');
bem.SimpleTable__row = makeBem(bem.SimpleTable, 'row', 'tr');
// NOTE: messageRow needs a __cell with colspan set
bem.SimpleTable__messageRow = makeBem(bem.SimpleTable, 'message-row', 'tr');
bem.SimpleTable__cell = makeBem(bem.SimpleTable, 'cell', 'td');

bem.tagSelect = makeBem(null, 'tag-select');
bem.collectionFilter = makeBem(null, 'collection-filter');

Expand Down
27 changes: 27 additions & 0 deletions jsapp/js/components/common/ButtonNew.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {Button as ButtonMantine, createPolymorphicComponent, Tooltip} from '@mantine/core';
import type {ButtonProps as ButtonPropsMantine, TooltipProps} from '@mantine/core/lib/components';
import {forwardRef} from 'react';

// See boilerpate at: https://mantine.dev/guides/polymorphic/#wrapping-polymorphic-components

export interface ButtonProps extends ButtonPropsMantine {
tooltip?: React.ReactNode;
tooltipProps?: Partial<Omit<TooltipProps, 'label'>>;
}

const Button = forwardRef<HTMLButtonElement, ButtonProps>(({tooltip, tooltipProps, ...others}, ref) => {
if (!tooltip) {
return (
<ButtonMantine {...others} ref={ref} />
);
}

return (
<Tooltip label={tooltip} {...tooltipProps}>
<ButtonMantine {...others} ref={ref} />
</Tooltip>
);
});
Button.displayName = 'Button';

export default createPolymorphicComponent<'button', ButtonProps>(Button);
25 changes: 25 additions & 0 deletions jsapp/js/components/common/SimpleTable.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// We need bigger specificity in each selector to ensure we do overwrite default
// styles

table.SimpleTableRoot {
background-color: var(--mantine-color-gray-9);
border-collapse: separate;
border-radius: var(--mantine-radius-md);
}

thead.SimpleTableThead {
background-color: var(--mantine-color-gray-8);
}

th.SimpleTableTh {
font-size: var(--mantine-font-size-sm);
color: var(--mantine-color-gray-2);
font-weight: 400;
}

td.SimpleTableTd {
font-size: var(--mantine-font-size-md);
border-top-width: 1px;
border-top-color: var(--mantine-color-gray-7);
border-top-style: solid;
}
46 changes: 46 additions & 0 deletions jsapp/js/components/common/SimpleTable.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type {Meta, StoryObj} from '@storybook/react';
import SimpleTable from './SimpleTable';

const meta: Meta<React.ComponentProps<typeof SimpleTable>> = {
title: 'common/SimpleTable',
component: SimpleTable,
argTypes: {},
args: {},
render: ({...args}) => (
<SimpleTable
{...args}
head={['Element position', 'Atomic mass', 'Symbol', 'Element name']}
body={
[
[6, 12.011, 'C', 'Carbon'],
[7, 14.007, 'N', 'Nitrogen'],
[39, 88.906, 'Y', 'Yttrium'],
[56, 137.33, 'Ba', 'Barium'],
[
'n/a',
'n/a',
'??',
(
<div key='test'>
This is just a DIV. It has a button and an input:
<br/><br/>
<button>button</button>
<br/><br/>
<input type='email'/>
<br/><br/>
It shows you can have any <code>React.ReactNode</code> here.
</div>
),
],
[58, 140.12, 'Ce', 'Cerium'],
]
}
/>
),
};

export default meta;

export const Primary: StoryObj<typeof SimpleTable> = {
args: {},
};
47 changes: 47 additions & 0 deletions jsapp/js/components/common/SimpleTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {Table, type MantineStyleProps, type TableData} from '@mantine/core';
import styles from './SimpleTable.module.scss';

interface SimpleTableProps extends MantineStyleProps {
head: TableData['head'];
body: TableData['body'];
/**
* Passing minimum width enables contextual horizontal scrollbar (i.e. without
* it the table will never display scrollbar - regardless of how small
* the screen is).
*/
minWidth?: number;
}

/**
* A wrapper component for `Table` from `@mantine/core`. It requires column
* headings, column data, and has optional minimum width. You can pass all
* standard Mantine style props down to the inner `Table`.
*/
export default function SimpleTable(
{head, body, minWidth, ...styleProps}: SimpleTableProps
) {
const table = (
<Table
{...styleProps}
classNames={{
table: styles.SimpleTableRoot,
thead: styles.SimpleTableThead,
th: styles.SimpleTableTh,
td: styles.SimpleTableTd,
}}
data={{head: head, body: body}}
horizontalSpacing='sm'
verticalSpacing='sm'
/>
);

if (minWidth) {
return (
<Table.ScrollContainer minWidth={minWidth} type='native'>
{table}
</Table.ScrollContainer>
);
}

return table;
}
Loading
Loading