Skip to content

Commit

Permalink
Merge pull request #83 from rebeccaalpert/preview-attachment
Browse files Browse the repository at this point in the history
feat(PreviewAttachment): add Preview Attachment component
  • Loading branch information
nicolethoen authored Aug 22, 2024
2 parents c21a1fe + c0cf395 commit b190a85
Show file tree
Hide file tree
Showing 11 changed files with 291 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export const BasicDemo: React.FunctionComponent = () => {
handleModalToggle={handleModalToggle}
isModalOpen={isModalOpen}
onCancel={() => null}
onSave={() => null}
// eslint-disable-next-line no-console
onSave={(_event, code) => console.log(`The new code is "${code}"`)}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
# Sidenav top-level section
# should be the same for all markdown files
section: extensions
subsection: Chat bots / AI
# Sidenav secondary level section
# should be the same for all markdown files
id: Preview attachment
# Tab (react | react-demos | html | html-demos | design-guidelines | accessibility)
source: react
# If you use typescript, the name of the interface to display props for
# These are found through the sourceProps function provided in patternfly-docs.source.js
propComponents: ['PreviewAttachment']
---

import PreviewAttachment from '@patternfly/virtual-assistant/dist/dynamic/PreviewAttachment';

### Basic example

```js file="./PreviewAttachment.tsx"

```
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { Button } from '@patternfly/react-core';
import { PreviewAttachment } from '@patternfly/virtual-assistant/dist/dynamic/PreviewAttachment';

export const BasicDemo: React.FunctionComponent = () => {
const [isModalOpen, setIsModalOpen] = React.useState(false);

const handleModalToggle = (_event: React.MouseEvent | MouseEvent | KeyboardEvent) => {
setIsModalOpen(!isModalOpen);
};

return (
<>
<Button onClick={handleModalToggle}>Launch modal</Button>
<PreviewAttachment
code="I am a code snippet"
fileName="test.yaml"
handleModalToggle={handleModalToggle}
isModalOpen={isModalOpen}
onDismiss={() => null}
onEdit={() => null}
/>
</>
);
};
99 changes: 17 additions & 82 deletions packages/module/src/AttachmentEdit/AttachmentEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,7 @@
// Attachment Edit - Chatbot Code Snippet Editor
// ============================================================================
import React from 'react';
import path from 'path';

// Import PatternFly components
import { CodeEditor, Language } from '@patternfly/react-code-editor';
import {
Button,
Flex,
Icon,
Modal,
ModalBody,
ModalFooter,
ModalHeader,
Stack,
StackItem
} from '@patternfly/react-core';
import { CodeIcon } from '@patternfly/react-icons';
import CodeModal from '../CodeModal';

export interface AttachmentEditProps {
/** Text shown in code editor */
Expand All @@ -26,8 +11,8 @@ export interface AttachmentEditProps {
fileName: string;
/** Function that runs when cancel button is clicked */
onCancel: (event: React.MouseEvent | MouseEvent | KeyboardEvent) => void;
/** Function that runs when save button is clicked */
onSave: (event: React.MouseEvent | MouseEvent | KeyboardEvent) => void;
/** Function that runs when save button is clicked; allows consumers to use the edited code string */
onSave: (event: React.MouseEvent | MouseEvent | KeyboardEvent, code: string) => void;
/** Function that opens and closes modal */
handleModalToggle: (event: React.MouseEvent | MouseEvent | KeyboardEvent) => void;
/** Whether modal is open */
Expand All @@ -43,80 +28,30 @@ export const AttachmentEdit: React.FunctionComponent<AttachmentEditProps> = ({
isModalOpen,
onCancel,
onSave,
title = 'Edit attachment',
...props
title = 'Edit attachment'
}: AttachmentEditProps) => {
const handleSave = (_event: React.MouseEvent | MouseEvent | KeyboardEvent) => {
const handleSave = (_event: React.MouseEvent | MouseEvent | KeyboardEvent, code) => {
handleModalToggle(_event);
onSave(_event);
onSave(_event, code);
};

const handleCancel = (_event: React.MouseEvent | MouseEvent | KeyboardEvent) => {
handleModalToggle(_event);
onCancel(_event);
};

const onEditorDidMount = (editor, monaco) => {
editor.layout();
editor.focus();
monaco.editor.getModels()[0].updateOptions({ tabSize: 5 });
};

return (
<Modal
isOpen={isModalOpen}
onClose={handleModalToggle}
ouiaId="EditAttachmentModal"
aria-labelledby="edit-attachment-title"
aria-describedby="edit-attachment-modal"
width="25%"
>
<ModalHeader title={title} labelId="edit-attachment-title" />
<ModalBody id="edit-attachment-body">
<Stack hasGutter>
<StackItem>
<Flex>
<Flex
className="pf-chatbot__attachment-icon"
justifyContent={{ default: 'justifyContentCenter' }}
alignItems={{ default: 'alignItemsCenter' }}
alignSelf={{ default: 'alignSelfCenter' }}
>
<Icon>
<CodeIcon color="white" />
</Icon>
</Flex>
<Stack>
<StackItem>{path.parse(fileName).name}</StackItem>
<StackItem className="pf-chatbot__attachment-language">
{Language[path.extname(fileName).slice(1)].toUpperCase()}
</StackItem>
</Stack>
</Flex>
</StackItem>
<StackItem>
<CodeEditor
isDarkTheme
isLineNumbersVisible
isLanguageLabelVisible
code={code}
language={Language[path.extname(fileName).slice(1)]}
onEditorDidMount={onEditorDidMount}
height="400px"
{...props}
/>
</StackItem>
</Stack>
</ModalBody>
<ModalFooter>
<Button isBlock key="confirm" variant="primary" onClick={handleSave}>
Save
</Button>
<Button isBlock key="cancel" variant="secondary" onClick={handleCancel}>
Cancel
</Button>
</ModalFooter>
</Modal>
<CodeModal
code={code}
fileName={fileName}
handleModalToggle={handleModalToggle}
isModalOpen={isModalOpen}
onPrimaryAction={handleSave}
onSecondaryAction={handleCancel}
primaryActionBtn="Save"
secondaryActionBtn="Cancel"
title={title}
/>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
.pf-chatbot__attachment-language {
.pf-chatbot__code-language {
color: var(--pf-t--global--text--color--subtle);
font-size: var(--pf-t--global--icon--size--font--xs);
}

.pf-chatbot__attachment-icon {
.pf-chatbot__code-icon {
background-color: var(--pf-t--global--icon--color--status--custom--default);
border-radius: var(--pf-t--global--border--radius--tiny);
width: 24px;
Expand Down
150 changes: 150 additions & 0 deletions packages/module/src/CodeModal/CodeModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// ============================================================================
// Code Modal - Chatbot Modal with Code Editor
// ============================================================================
import React, { useState } from 'react';
import path from 'path';

// Import PatternFly components
import { CodeEditor, Language } from '@patternfly/react-code-editor';
import {
Button,
Flex,
Icon,
Modal,
ModalBody,
ModalFooter,
ModalHeader,
Stack,
StackItem
} from '@patternfly/react-core';
import { CodeIcon } from '@patternfly/react-icons';

export interface CodeModalProps {
/** Text shown in code editor */
code: string;
/** Filename, including extension, of file shown in editor */
fileName: string;
/** Whether copying code is allowed */
isCopyEnabled?: boolean;
/** Whether code is read-only */
isReadOnly?: boolean;
/** Action assigned to primary modal button */
onPrimaryAction: (event: React.MouseEvent | MouseEvent | KeyboardEvent, code?: string) => void;
/** Action assigned to secondary modal button */
onSecondaryAction: (event: React.MouseEvent | MouseEvent | KeyboardEvent) => void;
/** Name of primary modal button */
primaryActionBtn: string;
/** Name of secondary modal button */
secondaryActionBtn: string;
/** Function that handles modal toggle */
handleModalToggle: (event: React.MouseEvent | MouseEvent | KeyboardEvent) => void;
/** Whether modal is open */
isModalOpen: boolean;
/** Title of modal */
title: string;
}

export const CodeModal: React.FunctionComponent<CodeModalProps> = ({
fileName,
code,
handleModalToggle,
isCopyEnabled,
isModalOpen,
isReadOnly,
onPrimaryAction,
onSecondaryAction,
primaryActionBtn,
secondaryActionBtn,
title,
...props
}: CodeModalProps) => {
const [newCode, setNewCode] = useState(code);

const handlePrimaryAction = (_event: React.MouseEvent | MouseEvent | KeyboardEvent) => {
handleModalToggle(_event);
if (!isReadOnly) {
onPrimaryAction(_event, newCode);
} else {
onPrimaryAction(_event);
}
};

const handleSecondaryAction = (_event: React.MouseEvent | MouseEvent | KeyboardEvent) => {
handleModalToggle(_event);
onSecondaryAction(_event);
};

const onEditorDidMount = (editor, monaco) => {
editor.layout();
editor.focus();
monaco.editor.getModels()[0].updateOptions({ tabSize: 5 });
};

const onCodeChange = (value: string) => {
if (!isReadOnly) {
setNewCode(value);
}
};

return (
<Modal
isOpen={isModalOpen}
onClose={handleModalToggle}
ouiaId="CodeModal"
aria-labelledby="code-modal-title"
aria-describedby="code-modal"
width="25%"
>
<ModalHeader title={title} labelId="code-modal-title" />
<ModalBody id="code-modal-body">
<Stack hasGutter>
<StackItem>
<Flex>
<Flex
className="pf-chatbot__code-icon"
justifyContent={{ default: 'justifyContentCenter' }}
alignItems={{ default: 'alignItemsCenter' }}
alignSelf={{ default: 'alignSelfCenter' }}
>
<Icon>
<CodeIcon color="white" />
</Icon>
</Flex>
<Stack>
<StackItem>{path.parse(fileName).name}</StackItem>
<StackItem className="pf-chatbot__code-language">
{Language[path.extname(fileName).slice(1)].toUpperCase()}
</StackItem>
</Stack>
</Flex>
</StackItem>
<StackItem>
<CodeEditor
isDarkTheme
isLineNumbersVisible
isLanguageLabelVisible
isCopyEnabled={isCopyEnabled}
isReadOnly={isReadOnly}
code={newCode}
language={Language[path.extname(fileName).slice(1)]}
onEditorDidMount={onEditorDidMount}
height="400px"
onCodeChange={onCodeChange}
{...props}
/>
</StackItem>
</Stack>
</ModalBody>
<ModalFooter>
<Button isBlock key="code-modal-primary" variant="primary" onClick={handlePrimaryAction} form="code-modal-form">
{primaryActionBtn}
</Button>
<Button isBlock key="code-modal-secondary" variant="secondary" onClick={handleSecondaryAction}>
{secondaryActionBtn}
</Button>
</ModalFooter>
</Modal>
);
};

export default CodeModal;
3 changes: 3 additions & 0 deletions packages/module/src/CodeModal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default } from './CodeModal';

export * from './CodeModal';
Loading

0 comments on commit b190a85

Please sign in to comment.