Skip to content

Commit

Permalink
feat: Refactored draftjs editor and added placeholder plugin, data pr…
Browse files Browse the repository at this point in the history
…ivacy edit for agency
  • Loading branch information
web-mi committed Jan 15, 2024
1 parent 0ee731a commit bab2654
Show file tree
Hide file tree
Showing 44 changed files with 1,884 additions and 60 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
.env.development.local
.env.test.local
.env.production.local
/.vite/

npm-debug.log*
yarn-debug.log*
Expand Down
3 changes: 3 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
<meta name="theme-color" content="#ffffff" />

<title>Online-Beratung Admin</title>
<script>
const global = globalThis;
</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"homepage": "http://tenant1.onlineberatung.local/admin",
"dependencies": {
"@ant-design/icons": "^4.7.0",
"@draft-js-plugins/editor": "^4.1.4",
"@draft-js-plugins/static-toolbar": "^4.1.4",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@mui/icons-material": "^5.8.4",
Expand All @@ -13,9 +15,12 @@
"axios": "^0.25.0",
"classnames": "^2.3.2",
"clsx": "^1.1.1",
"draft-js": ">=0.10.0",
"draft-js-export-html": "^1.4.1",
"hi-base32": "0.5.1",
"i18next": "^21.6.11",
"i18next-browser-languagedetector": "^6.1.3",
"immutable": "^3.8.1",
"lodash.merge": "^4.6.2",
"lodash.mergewith": "^4.6.2",
"lodash.set": "^4.3.2",
Expand Down
2 changes: 2 additions & 0 deletions src/api/agency/updateAgencyData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ export const updateAgencyData = async (agencyModel: AgencyData, formInput: Agenc
external: false,
demographics: formInput.demographics,
counsellingRelations: formInput.counsellingRelations,
dataProtection: formInput.dataProtection,
};

return fetchData({
url: `${agencyEndpointBase}/${agencyModel.id}`,
method: FETCH_METHODS.PUT,
Expand Down
19 changes: 9 additions & 10 deletions src/components/FormRichTextEditorField/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Form } from 'antd';
import DisabledContext from 'antd/es/config-provider/DisabledContext';
import classNames from 'classnames';
import { useCallback, useContext, useState } from 'react';
import { useContext, useState } from 'react';
import { useTranslation } from 'react-i18next';
import RichTextEditor from '../RichText/RichTextEditor';
import styles from './styles.module.scss';
Expand All @@ -12,15 +12,17 @@ interface FormRichTextEditorFieldProps {
required?: boolean;
placeholderKey?: string;
name?: string | string[];
placeholders?: { [key: string]: string };
}

interface FormRichTextEditorProps {
onFocus?: () => void;
onBlur?: () => void;
onChange?: (value: boolean) => void;
onChange?: (value: string) => void;
value?: boolean;
placeholderKey: string;
className?: string;
placeholders?: { [key: string]: string };
}

const FormRichTextEditor = ({
Expand All @@ -30,26 +32,21 @@ const FormRichTextEditor = ({
value,
className,
placeholderKey,
placeholders,
}: FormRichTextEditorProps) => {
const { t } = useTranslation();
const contextDisabled = useContext(DisabledContext);

const onChangeLocal = useCallback((ev) => {
const html = ev.toString('html');
// Rich editor when we click even if it's empty it creates the content
// '<p><br></p>' so when that happens we still want to validate as empty
onChange(html === '<p><br></p>' ? '' : html);
}, []);

return (
<RichTextEditor
className={classNames(className)}
onChange={onChangeLocal}
onChange={onChange}
onBlur={onBlur}
onFocus={onFocus}
value={value || ''}
disabled={contextDisabled}
placeholder={placeholderKey ? t(placeholderKey) : undefined}
placeholders={placeholders}
/>
);
};
Expand All @@ -59,6 +56,7 @@ export const FormRichTextEditorField = ({
labelKey,
required,
placeholderKey,
placeholders,
}: FormRichTextEditorFieldProps) => {
const { t } = useTranslation();
const [focused, setFocused] = useState(false);
Expand All @@ -79,6 +77,7 @@ export const FormRichTextEditorField = ({
onBlur={() => setFocused(false)}
placeholderKey={placeholderKey}
className={styles.input}
placeholders={placeholders}
/>
</Form.Item>
);
Expand Down
10 changes: 6 additions & 4 deletions src/components/Page/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { ChevronLeft } from '@mui/icons-material';
import { Spin } from 'antd';
import Title from 'antd/es/typography/Title';
import classNames from 'classnames';
import React, { forwardRef, LegacyRef, useMemo } from 'react';
import React, { cloneElement, forwardRef, LegacyRef, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { NavLink } from 'react-router-dom';
import SvgIcon from '@mui/material/SvgIcon/SvgIcon';

Check warning on line 8 in src/components/Page/index.tsx

View workflow job for this annotation

GitHub Actions / Build & Release

'SvgIcon' is defined but never used

Check warning on line 8 in src/components/Page/index.tsx

View workflow job for this annotation

GitHub Actions / ESLint

src/components/Page/index.tsx#L8

'SvgIcon' is defined but never used (@typescript-eslint/no-unused-vars)
import styles from './styles.module.scss';

interface PageProps {
Expand All @@ -26,7 +27,7 @@ interface PageBackProps {
titleKey?: string;
path: string;
children?: React.ReactChild | React.ReactChild[];
tabs?: Array<{ to: string; titleKey: string }>;
tabs?: Array<{ to: string; titleKey: string; icon?: JSX.Element }>;
}

export const Page = ({ children, stickyHeader, isLoading }: PageProps) => {
Expand All @@ -42,16 +43,17 @@ export const Page = ({ children, stickyHeader, isLoading }: PageProps) => {
);
};

const PageTabs = ({ tabs }: { tabs: Array<{ to: string; titleKey }> }) => {
const PageTabs = ({ tabs }: { tabs: Array<{ to: string; titleKey; icon?: JSX.Element }> }) => {
const { t } = useTranslation();

return (
<div className={styles.tabsContainer}>
{tabs
?.filter((tab) => tab && tab.to)
.map((tab) => (
.map(({ icon, ...tab }) => (
<NavLink className={styles.tab} to={tab.to} key={tab.titleKey}>
{t(tab.titleKey)}
{icon && cloneElement(icon, { className: styles.tabIcon })}
</NavLink>
))}
</div>
Expand Down
5 changes: 5 additions & 0 deletions src/components/Page/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@
border-bottom: 4px solid transparent;
font-size: 16px;
line-height: 24px;
display: inline-flex;
}

.tabIcon {
margin-left: 4px;
}

.titleContainer {
Expand Down
65 changes: 65 additions & 0 deletions src/components/RichText/BlockStyleButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* eslint-disable react/no-children-prop */
import clsx from 'clsx';
import { EditorState, RichUtils } from 'draft-js';
import React, { MouseEvent, ReactNode, useCallback, useMemo } from 'react';
import { DraftJsStyleButtonProps } from '@draft-js-plugins/buttons';

Check failure on line 5 in src/components/RichText/BlockStyleButton.tsx

View workflow job for this annotation

GitHub Actions / Build & Release

'@draft-js-plugins/buttons' should be listed in the project's dependencies. Run 'npm i -S @draft-js-plugins/buttons' to add it

Check failure on line 5 in src/components/RichText/BlockStyleButton.tsx

View workflow job for this annotation

GitHub Actions / Build & Release

'@draft-js-plugins/buttons' should be listed in the project's dependencies. Run 'npm i -S @draft-js-plugins/buttons' to add it

Check failure on line 5 in src/components/RichText/BlockStyleButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint

src/components/RichText/BlockStyleButton.tsx#L5

'@draft-js-plugins/buttons' should be listed in the project's dependencies. Run 'npm i -S @draft-js-plugins/buttons' to add it (import/no-extraneous-dependencies)
import { Button } from 'antd';
import { ButtonType } from 'antd/lib/button/button';

interface ButtonProps extends DraftJsStyleButtonProps {
type: ButtonType;
}

interface CreateBlockStyleButtonProps extends Omit<DraftJsStyleButtonProps, 'buttonProps'> {
blockType: string;
children: ReactNode;
editorState: EditorState;
buttonProps?: ButtonProps;
}

const BlockStyleButton = ({
blockType,
children,
theme,
buttonProps,
setEditorState,
editorState,
}: CreateBlockStyleButtonProps) => {
const toggleStyle = useCallback(
(event: MouseEvent): void => {
event.preventDefault();
setEditorState(RichUtils.toggleBlockType(editorState, blockType));
},
[editorState, setEditorState],
);

const preventBubblingUp = useCallback((event: MouseEvent): void => {
event.preventDefault();
}, []);

const blockTypeIsActive = useMemo((): boolean => {
// if the button is rendered before the editor
if (!editorState) {
return false;
}

const type = editorState.getCurrentContent().getBlockForKey(editorState.getSelection().getStartKey()).getType();
return type === blockType;
}, [editorState]);

const className = blockTypeIsActive ? clsx(theme.button, theme.active) : theme.button;

return (
<Button
children={children}
className={className}
onMouseDown={preventBubblingUp}
onClick={toggleStyle}
size="small"
role="button"
aria-label={`create ${blockType}`}
{...buttonProps}
/>
);
};
export default BlockStyleButton;
59 changes: 59 additions & 0 deletions src/components/RichText/InlineStyleButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* eslint-disable react/no-children-prop */
import clsx from 'clsx';
import { EditorState, RichUtils } from 'draft-js';
import React, { MouseEvent, ReactNode, useCallback, useMemo } from 'react';
import { DraftJsStyleButtonProps } from '@draft-js-plugins/buttons';

Check failure on line 5 in src/components/RichText/InlineStyleButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint

src/components/RichText/InlineStyleButton.tsx#L5

'@draft-js-plugins/buttons' should be listed in the project's dependencies. Run 'npm i -S @draft-js-plugins/buttons' to add it (import/no-extraneous-dependencies)
import { Button } from 'antd';
import { ButtonType } from 'antd/lib/button/button';

interface ButtonProps extends DraftJsStyleButtonProps {
type: ButtonType;
}

interface CreateInlineStyleButtonProps extends Omit<DraftJsStyleButtonProps, 'buttonProps'> {
inlineStyle: string;
children: ReactNode;
editorState: EditorState;
buttonProps?: ButtonProps;
}

const InlineStyleButton = ({
inlineStyle,
children,
theme,
buttonProps,
setEditorState,
editorState,
}: CreateInlineStyleButtonProps) => {
const toggleStyle = useCallback(
(event: MouseEvent): void => {
event.preventDefault();
setEditorState(RichUtils.toggleInlineStyle(editorState, inlineStyle));
},
[editorState, setEditorState],
);

const preventBubblingUp = useCallback((event: MouseEvent): void => {
event.preventDefault();
}, []);

const styleIsActive = useMemo((): boolean => {
return editorState && editorState.getCurrentInlineStyle().has(inlineStyle);
}, [editorState]);

const className = styleIsActive ? clsx(theme.button, theme.active) : theme.button;

return (
<Button
children={children}
className={className}
onMouseDown={preventBubblingUp}
onClick={toggleStyle}
size="small"
role="button"
aria-label={`${inlineStyle} text`}
{...buttonProps}
/>
);
};
export default InlineStyleButton;
Loading

0 comments on commit bab2654

Please sign in to comment.