Unverified Commit 25216719 authored by Yue Yang's avatar Yue Yang Committed by GitHub
Browse files

refactor(ui): rewrite form fields (#3107)


* refactor: rewrite form fields

1. The theme palette has been updated because of the new form fields.
2. Extracts old `Scope` component to `components/` for next use.
Signed-off-by: default avatarYue Yang <g1enyy0ung@gmail.com>

* fix: license checker
Signed-off-by: default avatarYue Yang <g1enyy0ung@gmail.com>

* chore: address comments
Signed-off-by: default avatarYue Yang <g1enyy0ung@gmail.com>
Co-authored-by: default avatarTi Chi Robot <ti-community-prow-bot@tidb.io>
Showing with 306 additions and 160 deletions
+306 -160
/*
* Copyright 2022 Chaos Mesh Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { Theme } from '@mui/material/styles'
// TODO: remove this declaration when @mui/styles isn't used anymore. See: https://mui.com/guides/migration-v4/.
declare module '@mui/styles' {
interface DefaultTheme extends Theme {}
}
declare module '@mui/material/styles' {
interface Palette {
secondaryContainer: Palette['primary']
onSecondaryContainer: Palette['primary']
onSurfaceVariant: Palette['primary']
outline: Palette['primary']
}
interface PaletteOptions {
secondaryContainer: PaletteOptions['primary']
onSecondaryContainer: PaletteOptions['primary']
onSurfaceVariant: PaletteOptions['primary']
outline: PaletteOptions['primary']
}
}
/*
* Copyright 2021 Chaos Mesh Authors.
* Copyright 2022 Chaos Mesh Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -14,61 +14,48 @@
* limitations under the License.
*
*/
import { Autocomplete, Chip, TextField, TextFieldProps } from '@mui/material'
import { getIn, useFormikContext } from 'formik'
import Paper from '@ui/mui-extends/esm/Paper'
import i18n from 'components/T'
import MuiExtendsAutocompleteField from '@ui/mui-extends/esm/AutocompleteField'
import type { AutocompleteFieldProps as MuiExtendsAutocompleteFieldProps } from '@ui/mui-extends/esm/AutocompleteField'
import { T } from 'components/T'
interface AutocompleteMultipleFieldProps {
options: string[]
interface AutocompleteFieldProps extends MuiExtendsAutocompleteFieldProps {
name: string
}
const AutocompleteMultipleField: React.FC<AutocompleteMultipleFieldProps & TextFieldProps> = ({
options,
...props
}) => {
const AutocompleteField: React.FC<AutocompleteFieldProps> = ({ name, multiple, options, ...props }) => {
const { values, setFieldValue } = useFormikContext()
const value = getIn(values, name)
const name = props.name!
const labels: string[] = getIn(values, name)
const setLabels = (labels: string[]) => setFieldValue(name, labels)
const onChange = (_: any, newVal: string[], reason: string) => {
const onChange = (_: any, newVal: string | string[] | null, reason: string) => {
if (reason === 'clear') {
setLabels([])
setFieldValue(name, multiple ? [] : '')
return
}
setLabels(newVal)
setFieldValue(name, newVal)
}
const onDelete = (val: string) => () => setLabels(labels.filter((d) => d !== val))
const onDelete = (val: string) => () =>
setFieldValue(
name,
value.filter((d: string) => d !== val)
)
return (
<Autocomplete
freeSolo
multiple
<MuiExtendsAutocompleteField
{...props}
multiple={multiple}
options={!props.disabled ? options : []}
noOptionsText={i18n('common.noOptions')}
value={labels}
noOptionsText={<T id="common.noOptions" />}
value={value}
onChange={onChange}
renderTags={(value: string[], getTagProps) =>
value.map((val: string, index: number) => (
<Chip
{...getTagProps({ index })}
style={{ height: 24 }}
label={val}
color="primary"
onDelete={onDelete(val)}
/>
))
}
renderInput={(params) => <TextField {...params} {...props} size="small" fullWidth />}
PaperComponent={(props) => <Paper {...props} sx={{ p: 0 }} />}
onRenderValueDelete={onDelete}
/>
)
}
export default AutocompleteMultipleField
export default AutocompleteField
......@@ -14,51 +14,20 @@
* limitations under the License.
*
*/
import { Box, Chip, TextField, TextFieldProps } from '@mui/material'
import { Field, getIn, useFormikContext } from 'formik'
import MuiExtendsSelectField, { SelectFieldProps } from '@ui/mui-extends/esm/SelectField'
const SelectField: React.FC<TextFieldProps & { multiple?: boolean }> = ({ multiple = false, ...props }) => {
function SelectField<T>(props: SelectFieldProps<T>) {
const { values, setFieldValue } = useFormikContext()
const onDelete = (val: string) => () =>
setFieldValue(
props.name!,
getIn(values, props.name!).filter((d: string) => d !== val)
(getIn(values, props.name!) as string[]).filter((d) => d !== val)
)
const SelectProps = {
multiple,
renderValue: multiple
? (selected: any) => (
<Box display="flex" flexWrap="wrap" mt={1}>
{(selected as string[]).map((val) => (
<Chip
key={val}
style={{ height: 24, margin: 1 }}
label={val}
color="primary"
onDelete={onDelete(val)}
onMouseDown={(e) => e.stopPropagation()}
/>
))}
</Box>
)
: undefined,
}
const rendered = (
<Field
{...props}
className={props.className}
as={TextField}
select
size="small"
fullWidth
SelectProps={SelectProps}
/>
)
return rendered
return <Field {...props} as={MuiExtendsSelectField} onRenderValueDelete={onDelete} />
}
export default SelectField
......@@ -15,19 +15,13 @@
*
*/
import { FastField, Field, FieldValidator } from 'formik'
import { TextField as MUITextField, TextFieldProps } from '@mui/material'
import MuiExtendsTextField, { TextFieldProps } from '@ui/mui-extends/esm/TextField'
const TextField: React.FC<TextFieldProps & { validate?: FieldValidator; fast?: boolean }> = ({
fast = false,
...rest
}) => {
const rendered = fast ? (
<FastField {...rest} as={MUITextField} size="small" fullWidth />
) : (
<Field {...rest} as={MUITextField} size="small" fullWidth />
)
return rendered
return fast ? <FastField {...rest} as={MuiExtendsTextField} /> : <Field {...rest} as={MuiExtendsTextField} />
}
export default TextField
......@@ -14,8 +14,9 @@
* limitations under the License.
*
*/
export { default as TextField } from './TextField'
export { default as SelectField } from './SelectField'
export { default as LabelField } from './LabelField'
export { default as AutocompleteMultipleField } from './AutocompleteMultipleField'
export { default as AutocompleteField } from './AutocompleteField'
export { default as Submit } from './Submit'
......@@ -14,15 +14,16 @@
* limitations under the License.
*
*/
import { Box, Button } from '@mui/material'
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'
import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp'
import Space from '@ui/mui-extends/esm/Space'
import i18n from 'components/T'
import { T } from 'components/T'
import { useState } from 'react'
interface OtherOptionsProps {
interface MoreOptionsProps {
isOpen?: boolean
beforeOpen?: () => void
afterClose?: () => void
......@@ -30,7 +31,7 @@ interface OtherOptionsProps {
disabled?: boolean
}
const OtherOptions: React.FC<OtherOptionsProps> = ({
const MoreOptions: React.FC<MoreOptionsProps> = ({
isOpen = false,
beforeOpen,
afterClose,
......@@ -59,12 +60,12 @@ const OtherOptions: React.FC<OtherOptionsProps> = ({
onClick={setOpen}
disabled={disabled}
>
{title ? title : i18n('common.otherOptions')}
{title ? title : <T id="common.moreOptions" />}
</Button>
</Box>
<Space sx={{ display: open ? 'unset' : 'none' }}>{children}</Space>
<Space sx={{ display: open ? 'flex' : 'none' }}>{children}</Space>
</Space>
)
}
export default OtherOptions
export default MoreOptions
......@@ -25,13 +25,13 @@ import { useEffect, useMemo, useState } from 'react'
import { useStoreDispatch, useStoreSelector } from 'store'
import CheckIcon from '@mui/icons-material/Check'
import Mode from './form/Mode'
import Mode from 'components/Scope/Mode'
import MoreOptions from 'components/MoreOptions'
import Nodes from './form/Nodes'
import OtherOptions from 'components/OtherOptions'
import Paper from '@ui/mui-extends/esm/Paper'
import PublishIcon from '@mui/icons-material/Publish'
import Scheduler from './form/Scheduler'
import Scope from './form/Scope'
import Scope from 'components/Scope'
import SkeletonN from '@ui/mui-extends/esm/SkeletonN'
import Space from '@ui/mui-extends/esm/Space'
import UndoIcon from '@mui/icons-material/Undo'
......@@ -170,7 +170,7 @@ const Step2: React.FC<Step2Props> = ({ inWorkflow = false, inSchedule = false })
/>
)}
{inSchedule && <ScheduleSpecificFields errors={errors} touched={touched} />}
<OtherOptions>
<MoreOptions>
{namespaces.length && (
<SelectField
name="metadata.namespace"
......@@ -186,7 +186,7 @@ const Step2: React.FC<Step2Props> = ({ inWorkflow = false, inSchedule = false })
)}
<LabelField name="metadata.labels" label={i18n('k8s.labels')} isKV />
<LabelField name="metadata.annotations" label={i18n('k8s.annotations')} isKV />
</OtherOptions>
</MoreOptions>
{!inWorkflow && (
<>
<Divider />
......
......@@ -119,9 +119,7 @@ const Kernel: React.FC<KernelProps> = ({ onSubmit }) => {
type="number"
name="failKernRequest.probability"
helperText="The fails with probability"
InputProps={{
endAdornment: <InputAdornment position="end">%</InputAdornment>,
}}
endAdornment={<InputAdornment position="end">%</InputAdornment>}
/>
<TextField type="number" name="failKernRequest.times" helperText="The max times of failures" />
</Space>
......
......@@ -18,7 +18,7 @@ import { Form, Formik, getIn } from 'formik'
import { LabelField, Submit, TextField } from 'components/FormField'
import { useEffect, useState } from 'react'
import OtherOptions from 'components/OtherOptions'
import MoreOptions from 'components/MoreOptions'
import Space from '@ui/mui-extends/esm/Space'
import { Typography } from '@mui/material'
import typesData from '../data/types'
......@@ -108,7 +108,7 @@ const Stress: React.FC<StressProps> = ({ onSubmit }) => {
/>
</Space>
<OtherOptions>
<MoreOptions>
<TextField
name="stressngStressors"
label="Options of stress-ng"
......@@ -119,7 +119,7 @@ const Stress: React.FC<StressProps> = ({ onSubmit }) => {
label="Container Name"
helperText="Optional. Type string and end with a space to generate the container names. If it's empty, the first container will be injected"
/>
</OtherOptions>
</MoreOptions>
<Submit />
</Form>
......
......@@ -14,7 +14,7 @@
* limitations under the License.
*
*/
import { AutocompleteMultipleField, LabelField, SelectField, Submit, TextField } from 'components/FormField'
import { AutocompleteField, LabelField, SelectField, Submit, TextField } from 'components/FormField'
import { Env, clearNetworkTargetPods } from 'slices/experiments'
import { Form, Formik, FormikErrors, FormikTouched, getIn, setIn } from 'formik'
import { Kind, Spec } from '../data/types'
......@@ -22,9 +22,9 @@ import { useEffect, useState } from 'react'
import { useStoreDispatch, useStoreSelector } from 'store'
import { MenuItem } from '@mui/material'
import MoreOptions from 'components/MoreOptions'
import { ObjectSchema } from 'yup'
import OtherOptions from 'components/OtherOptions'
import Scope from './Scope'
import Scope from 'components/Scope'
import Space from '@ui/mui-extends/esm/Space'
import basicData from '../data/basic'
import i18n from 'components/T'
......@@ -164,7 +164,8 @@ const TargetGenerated: React.FC<TargetGeneratedProps> = ({ env, kind, data, vali
)
case 'autocomplete':
return (
<AutocompleteMultipleField
<AutocompleteField
multiple
key={k}
name={k}
label={v.label}
......@@ -205,7 +206,7 @@ const TargetGenerated: React.FC<TargetGeneratedProps> = ({ env, kind, data, vali
<Form>
<Space>{parseDataToFormFields(errors, touched)}</Space>
{env === 'k8s' && kind === 'NetworkChaos' && (
<OtherOptions
<MoreOptions
title={i18n('newE.target.network.target.title')}
beforeOpen={beforeTargetOpen}
afterClose={afterTargetClose}
......@@ -219,7 +220,7 @@ const TargetGenerated: React.FC<TargetGeneratedProps> = ({ env, kind, data, vali
podsPreviewDesc={i18n('newE.target.network.target.podsPreviewHelper')}
/>
)}
</OtherOptions>
</MoreOptions>
)}
<Submit />
</Form>
......
......@@ -68,9 +68,7 @@ export const Fields = ({ errors, touched }: Pick<FormikProps<FormikValues>, 'err
type="number"
name="spec.startingDeadlineSeconds"
label={i18n('newS.basic.startingDeadlineSeconds')}
InputProps={{
endAdornment: <InputAdornment position="end">{i18n('common.seconds')}</InputAdornment>,
}}
endAdornment={<InputAdornment position="end">{i18n('common.seconds')}</InputAdornment>}
helperText={
getIn(errors, 'spec.startingDeadlineSeconds') && getIn(touched, 'spec.startingDeadlineSeconds')
? getIn(errors, 'spec.startingDeadlineSeconds')
......
/*
* Copyright 2021 Chaos Mesh Authors.
* Copyright 2022 Chaos Mesh Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -16,11 +16,10 @@
*/
import { InputAdornment, MenuItem } from '@mui/material'
import { SelectField, TextField } from '../../FormField'
import { SelectField, TextField } from 'components/FormField'
import { getIn, useFormikContext } from 'formik'
import React from 'react'
import i18n from '../../T'
import { T } from 'components/T'
const modes = [
{ name: 'Random One', value: 'one' },
......@@ -31,19 +30,20 @@ const modes = [
const modesWithAdornment = ['fixed-percent', 'random-max-percent']
interface ModeProps {
disabled: boolean
modeScope: string
scope: string
disabled: boolean
}
const Mode: React.FC<ModeProps> = ({ disabled, modeScope, scope }) => {
const { values } = useFormikContext()
return (
<>
<SelectField
name={`${modeScope}.mode`}
label={i18n('newE.scope.mode')}
helperText={i18n('newE.scope.modeHelper')}
name={modeScope ? `${modeScope}.mode` : 'mode'}
label={<T id="newE.scope.mode" />}
helperText={<T id="newE.scope.modeHelper" />}
disabled={disabled}
>
<MenuItem value="all">All</MenuItem>
......@@ -56,14 +56,12 @@ const Mode: React.FC<ModeProps> = ({ disabled, modeScope, scope }) => {
{!['all', 'one'].includes(getIn(values, modeScope).mode) && (
<TextField
name={`${modeScope}.value`}
label={i18n('newE.scope.modeValue')}
helperText={i18n('newE.scope.modeValueHelper')}
InputProps={{
endAdornment: modesWithAdornment.includes(getIn(values, scope).mode) && (
<InputAdornment position="end">%</InputAdornment>
),
}}
name={modeScope ? `${modeScope}.value` : 'value'}
label={<T id="newE.scope.modeValue" />}
helperText={<T id="newE.scope.modeValueHelper" />}
endAdornment={
modesWithAdornment.includes(getIn(values, scope).mode) && <InputAdornment position="end">%</InputAdornment>
}
disabled={disabled}
/>
)}
......
/*
* Copyright 2021 Chaos Mesh Authors.
* Copyright 2022 Chaos Mesh Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -15,8 +15,8 @@
*
*/
import { AutocompleteMultipleField, SelectField } from 'components/FormField'
import { Divider, MenuItem, Typography } from '@mui/material'
import { AutocompleteField, SelectField } from 'components/FormField'
import { MenuItem, Typography } from '@mui/material'
import { arrToObjBySep, objToArrBySep } from 'lib/utils'
import {
getAnnotations,
......@@ -29,10 +29,11 @@ import { useEffect, useMemo } from 'react'
import { useStoreDispatch, useStoreSelector } from 'store'
import Mode from './Mode'
import OtherOptions from 'components/OtherOptions'
import MoreOptions from 'components/MoreOptions'
import ScopePodsTable from './ScopePodsTable'
import type { SelectChangeEvent } from '@mui/material'
import Space from '@ui/mui-extends/esm/Space'
import i18n from 'components/T'
import { T } from 'components/T'
interface ScopeProps {
namespaces: string[]
......@@ -42,7 +43,7 @@ interface ScopeProps {
podsPreviewDesc?: string | JSX.Element
}
const phases = [{ label: 'All', value: 'all' }, 'Pending', 'Running', 'Succeeded', 'Failed', 'Unknown']
const phases = ['Pending', 'Running', 'Succeeded', 'Failed', 'Unknown']
const Scope: React.FC<ScopeProps> = ({
namespaces,
......@@ -71,9 +72,9 @@ const Scope: React.FC<ScopeProps> = ({
const labelKVs = useMemo(() => objToArrBySep(labels, kvSeparator), [labels])
const annotationKVs = useMemo(() => objToArrBySep(annotations, kvSeparator), [annotations])
const handleChangeIncludeAll = (e: React.ChangeEvent<HTMLInputElement>) => {
const handleChangeIncludeAll = (e: SelectChangeEvent<string[]>) => {
const lastValues = getIn(values, e.target.name)
const currentValues = e.target.value as unknown as string[]
const currentValues = e.target.value as string[]
if (!lastValues.includes('all') && currentValues.includes('all')) {
e.target.value = ['all'] as any
......@@ -118,76 +119,74 @@ const Scope: React.FC<ScopeProps> = ({
return (
<Space>
<AutocompleteMultipleField
<AutocompleteField
multiple
name={`${scope}.namespaces`}
label={i18n('k8s.namespaceSelectors')}
label={<T id="k8s.namespaceSelectors" />}
helperText={
getIn(touched, `${scope}.namespaces`) && getIn(errors, `${scope}.namespaces`)
? getIn(errors, `${scope}.namespaces`)
: i18n('common.multiOptions')
getIn(touched, `${scope}.namespaces`) && getIn(errors, `${scope}.namespaces`) ? (
getIn(errors, `${scope}.namespaces`)
) : (
<T id="newE.scope.namespaceSelectorsHelper" />
)
}
options={!enableKubeSystemNS ? namespaces.filter((d) => d !== 'kube-system') : namespaces}
error={getIn(errors, `${scope}.namespaces`) && getIn(touched, `${scope}.namespaces`) ? true : false}
disabled={disabled}
/>
<AutocompleteMultipleField
<AutocompleteField
multiple
name={`${scope}.labelSelectors`}
label={i18n('k8s.labelSelectors')}
helperText={i18n('common.multiOptions')}
label={<T id="k8s.labelSelectors" />}
helperText={<T id="newE.scope.labelSelectorsHelper" />}
options={labelKVs}
disabled={disabled}
/>
<OtherOptions disabled={disabled}>
<AutocompleteMultipleField
<MoreOptions disabled={disabled}>
<AutocompleteField
multiple
name={`${scope}.annotationSelectors`}
label={i18n('k8s.annotationsSelectors')}
helperText={i18n('common.multiOptions')}
label={<T id="k8s.annotationSelectors" />}
helperText={<T id="newE.scope.annotationSelectorsHelper" />}
options={annotationKVs}
disabled={disabled}
/>
<SelectField
name={`${scope}.podPhaseSelectors`}
label={i18n('k8s.podPhaseSelectors')}
helperText={i18n('common.multiOptions')}
<SelectField<string[]>
multiple
name={`${scope}.podPhaseSelectors`}
label={<T id="k8s.podPhaseSelectors" />}
helperText={<T id="newE.scope.phaseSelectorsHelper" />}
onChange={handleChangeIncludeAll}
disabled={disabled}
fullWidth
>
{phases.map((option) =>
typeof option === 'string' ? (
<MenuItem key={option} value={option}>
{option}
</MenuItem>
) : (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
)
)}
<MenuItem value="all">All</MenuItem>
{phases.map((option) => (
<MenuItem key={option} value={option}>
{option}
</MenuItem>
))}
</SelectField>
</OtherOptions>
</MoreOptions>
<Divider />
<Typography>{i18n('newE.scope.mode')}</Typography>
<Mode disabled={disabled} modeScope={modeScope} scope={scope} />
<Divider />
<div>
<Typography sx={{ color: disabled ? 'text.disabled' : undefined }}>
{podsPreviewTitle || i18n('newE.scope.targetPodsPreview')}
<Typography variant="h6" fontWeight="bold" sx={{ color: disabled ? 'text.disabled' : undefined }}>
{podsPreviewTitle || <T id="newE.scope.targetPodsPreview" />}
</Typography>
<Typography variant="body2" sx={{ color: disabled ? 'text.disabled' : 'text.secondary' }}>
{podsPreviewDesc || i18n('newE.scope.targetPodsPreviewHelper')}
{podsPreviewDesc || <T id="newE.scope.targetPodsPreviewHelper" />}
</Typography>
</div>
{pods.length > 0 ? (
<ScopePodsTable scope={scope} pods={pods} />
) : (
<Typography variant="subtitle2" sx={{ color: disabled ? 'text.disabled' : undefined }}>
{i18n('newE.scope.noPodsFound')}
<Typography variant="body2" fontWeight="medium" sx={{ color: disabled ? 'text.disabled' : undefined }}>
<T id="newE.scope.noPodsFound" />
</Typography>
)}
</Space>
......
......@@ -50,13 +50,17 @@
"namespaceHelper": "Select the namespace"
},
"scope": {
"namespaceSelectorsHelper": "Specifies the namespaces to which target Pods belong.",
"labelSelectorsHelper": "Specifies the labels that target Pods come with.",
"annotationSelectorsHelper": "Specifies the annotations that target Pods come with.",
"phaseSelectorsHelper": "Specifies the phases of target Pods.",
"mode": "Mode",
"modeHelper": "Select the experiment mode",
"modeHelper": "Specifies the mode of the experiment.",
"modeValue": "Mode Value",
"modeValueHelper": "Please fill the mode value",
"targetPods": "Target Pods",
"targetPodsPreview": "Target Pods Preview",
"targetPodsPreviewHelper": "Checking or unchecking Pods to further limit the scope",
"targetPodsPreview": "Preview of Pods to be injected",
"targetPodsPreviewHelper": "Checking or unchecking Pods to further limit the scope.",
"noPodsFound": "No pods found"
},
"target": {
......@@ -310,7 +314,7 @@
},
"common": {
"add": "Add",
"otherOptions": "Other Options",
"moreOptions": "More Options",
"batchOperation": "Batch operation",
"cancel": "Cancel",
"cancelEdit": "Cancel edit",
......@@ -387,7 +391,7 @@
"annotations": "Annotations",
"namespaceSelectors": "Namespace Selectors",
"labelSelectors": "Label Selectors",
"annotationsSelectors": "Annotation Selectors",
"annotationSelectors": "Annotation Selectors",
"podPhaseSelectors": "Phase Selectors"
},
"physic": {
......
......@@ -50,6 +50,10 @@
"namespaceHelper": "选择命名空间"
},
"scope": {
"namespaceSelectorsHelper": "指定目标 Pod 所属的命名空间(namespace)。",
"labelSelectorsHelper": "指定目标 Pod 带有的标签(label)。",
"annotationSelectorsHelper": "指定目标 Pod 带有的注解(annotation)。",
"phaseSelectorsHelper": "指定目标 Pod 所处的阶段(phase)。",
"mode": "模式",
"modeHelper": "选择实验模式",
"modeValue": "模式值",
......@@ -308,7 +312,7 @@
},
"common": {
"add": "添加",
"otherOptions": "其他选项",
"moreOptions": "更多选项",
"batchOperation": "批量操作",
"cancel": "取消",
"cancelEdit": "取消修改",
......@@ -385,7 +389,7 @@
"annotations": "注解",
"namespaceSelectors": "命名空间选择器",
"labelSelectors": "标签选择器",
"annotationsSelectors": "注解选择器",
"annotationSelectors": "注解选择器",
"podPhaseSelectors": "阶段选择器"
},
"physic": {
......
......@@ -14,39 +14,59 @@
* limitations under the License.
*
*/
// All options are based on https://www.figma.com/file/2J6PVAaitQPQFHBtF5LbII/UI-Interface.
import { ThemeOptions, createTheme, responsiveFontSizes } from '@mui/material/styles'
const common: ThemeOptions = {
mixins: {
toolbar: {
minHeight: 56,
},
},
palette: {
primary: {
main: '#172d72',
},
secondary: {
main: '#d32f2f',
},
background: {
default: '#fafafa',
},
},
spacing: (factor: number) => `${0.25 * factor}rem`,
spacing: 4,
typography: {
// TODO: remove h2
h2: {
fontSize: '1.25rem',
fontWeight: 700,
},
// TODO: remove h3
h3: {
fontSize: '1.125rem',
fontWeight: 500,
},
button: {
textTransform: 'none',
},
},
}
const theme = responsiveFontSizes(createTheme(common))
// The light theme
const theme = responsiveFontSizes(
createTheme({
...common,
palette: {
primary: {
main: '#4159A9',
},
secondary: {
main: '#595D71',
},
secondaryContainer: {
main: '#DEE1F9',
},
onSecondaryContainer: {
main: '#161B2C',
},
background: {
default: '#fafafa',
},
onSurfaceVariant: {
main: '#45464E',
},
outline: {
main: '#76767F',
},
},
})
)
export const darkTheme = responsiveFontSizes(
createTheme({
......@@ -54,14 +74,26 @@ export const darkTheme = responsiveFontSizes(
palette: {
mode: 'dark',
primary: {
main: '#9db0eb',
main: '#B4C4FF',
},
secondary: {
main: '#f44336',
main: '#C1C5DC',
},
secondaryContainer: {
main: '#424659',
},
onSecondaryContainer: {
main: '#DEE1F9',
},
background: {
paper: '#424242',
default: '#303030',
paper: '#000',
default: '#1B1B1F',
},
onSurfaceVariant: {
main: '#C6C6D0',
},
outline: {
main: '#90909A',
},
},
})
......
// This file is synchronized from '@ui/app/src/theme.ts'.
// All options are based on https://www.figma.com/file/2J6PVAaitQPQFHBtF5LbII/UI-Interface.
import { ThemeProvider, createTheme, responsiveFontSizes } from '@mui/material/styles'
import React from 'react'
const common = {
spacing: 4,
}
// The light theme
const theme = responsiveFontSizes(
createTheme(common, {
palette: {
primary: {
main: '#4159A9',
},
secondary: {
main: '#595D71',
},
secondaryContainer: {
main: '#DEE1F9',
},
onSecondaryContainer: {
main: '#161B2C',
},
background: {
default: '#fafafa',
},
onSurfaceVariant: {
main: '#45464E',
},
},
})
)
export default function ({ children }) {
return <ThemeProvider theme={theme}>{children}</ThemeProvider>
}
......@@ -2,4 +2,7 @@ module.exports = {
stories: ['../stories/**/*.stories.mdx', '../stories/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: '@storybook/react',
features: {
emotionAlias: false, // https://stackoverflow.com/questions/70253373/mui-v5-storybook-theme-and-font-family-do-not-work-in-storybook
},
}
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet" />
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment