-
+
diff --git a/graph/client/src/app/ui-components/debounced-text-input.stories.tsx b/graph/client/src/app/ui-components/debounced-text-input.stories.tsx
new file mode 100644
index 0000000000..97bb92b64f
--- /dev/null
+++ b/graph/client/src/app/ui-components/debounced-text-input.stories.tsx
@@ -0,0 +1,26 @@
+import { ComponentStory, ComponentMeta } from '@storybook/react';
+import { DebouncedTextInput } from './debounced-text-input';
+
+const Story: ComponentMeta = {
+ component: DebouncedTextInput,
+ title: 'Shared/DebouncedTextInput',
+ argTypes: {
+ resetTextFilter: {
+ action: 'resetTextFilter',
+ },
+ updateTextFilter: {
+ action: 'updateTextFilter',
+ },
+ },
+};
+export default Story;
+
+const Template: ComponentStory = (args) => (
+
+);
+
+export const Primary = Template.bind({});
+Primary.args = {
+ currentText: '',
+ placeholderText: '',
+};
diff --git a/graph/client/src/app/ui-components/debounced-text-input.tsx b/graph/client/src/app/ui-components/debounced-text-input.tsx
new file mode 100644
index 0000000000..2d69dac492
--- /dev/null
+++ b/graph/client/src/app/ui-components/debounced-text-input.tsx
@@ -0,0 +1,85 @@
+import { KeyboardEvent, useEffect, useState } from 'react';
+import { useDebounce } from '../hooks/use-debounce';
+import { BackspaceIcon, FunnelIcon } from '@heroicons/react/24/outline';
+
+export interface DebouncedTextInputProps {
+ initialText: string;
+ placeholderText: string;
+ resetTextFilter: () => void;
+ updateTextFilter: (textFilter: string) => void;
+}
+
+export function DebouncedTextInput({
+ initialText,
+ placeholderText,
+ resetTextFilter,
+ updateTextFilter,
+}: DebouncedTextInputProps) {
+ const [currentTextFilter, setCurrentTextFilter] = useState(initialText ?? '');
+
+ const [debouncedValue, setDebouncedValue] = useDebounce(
+ currentTextFilter,
+ 500
+ );
+
+ function onTextFilterKeyUp(event: KeyboardEvent) {
+ if (event.key === 'Enter') {
+ updateTextFilter(event.currentTarget.value);
+ }
+ }
+
+ function onTextInputChange(change: string) {
+ if (change === '') {
+ setCurrentTextFilter('');
+ setDebouncedValue('');
+
+ resetTextFilter();
+ } else {
+ setCurrentTextFilter(change);
+ }
+ }
+
+ function resetClicked() {
+ setCurrentTextFilter('');
+ setDebouncedValue('');
+
+ resetTextFilter();
+ }
+
+ useEffect(() => {
+ updateTextFilter(debouncedValue);
+ }, [debouncedValue, updateTextFilter]);
+
+ return (
+
+ );
+}
+
+export default DebouncedTextInput;
diff --git a/graph/client/src/app/ui-components/dropdown.stories.tsx b/graph/client/src/app/ui-components/dropdown.stories.tsx
new file mode 100644
index 0000000000..6a35b01cba
--- /dev/null
+++ b/graph/client/src/app/ui-components/dropdown.stories.tsx
@@ -0,0 +1,20 @@
+import { ComponentStory, ComponentMeta } from '@storybook/react';
+import { Dropdown } from './dropdown';
+
+export default {
+ component: Dropdown,
+ title: 'Shared/Dropdown',
+ argTypes: {
+ onChange: { action: 'onChange' },
+ },
+} as ComponentMeta;
+
+const Template: ComponentStory = (args) => (
+
+
+
+
+);
+
+export const Primary = Template.bind({});
+Primary.args = {};
diff --git a/graph/client/src/app/ui-components/dropdown.tsx b/graph/client/src/app/ui-components/dropdown.tsx
new file mode 100644
index 0000000000..ddd6801d87
--- /dev/null
+++ b/graph/client/src/app/ui-components/dropdown.tsx
@@ -0,0 +1,20 @@
+/* eslint-disable-next-line */
+import React, { ReactNode } from 'react';
+
+export type DropdownProps = {
+ children: ReactNode[];
+} & React.HTMLAttributes;
+
+export function Dropdown(props: DropdownProps) {
+ const { className, children, ...rest } = props;
+ return (
+
+ );
+}
+
+export default Dropdown;
diff --git a/graph/client/src/app/ui-components/tag.stories.tsx b/graph/client/src/app/ui-components/tag.stories.tsx
new file mode 100644
index 0000000000..002090528c
--- /dev/null
+++ b/graph/client/src/app/ui-components/tag.stories.tsx
@@ -0,0 +1,15 @@
+import { ComponentStory, ComponentMeta } from '@storybook/react';
+import { Tag } from './tag';
+
+const Story: ComponentMeta = {
+ component: Tag,
+ title: 'Shared/Tag',
+};
+export default Story;
+
+const Template: ComponentStory = (args) => {args.text};
+
+export const Primary = Template.bind({});
+Primary.args = {
+ text: 'tag',
+};
diff --git a/graph/client/src/app/ui-components/tag.tsx b/graph/client/src/app/ui-components/tag.tsx
new file mode 100644
index 0000000000..2e262b5b97
--- /dev/null
+++ b/graph/client/src/app/ui-components/tag.tsx
@@ -0,0 +1,12 @@
+/* eslint-disable-next-line */
+export interface TagProps {}
+
+export function Tag(props) {
+ return (
+
+ {props.children}
+
+ );
+}
+
+export default Tag;
diff --git a/graph/client/src/styles.scss b/graph/client/src/styles.scss
index a1543850c0..370893e67f 100644
--- a/graph/client/src/styles.scss
+++ b/graph/client/src/styles.scss
@@ -71,35 +71,24 @@ canvas {
&[data-placement^='bottom'] > .tippy-arrow::before {
border-bottom-color: $gray;
}
+
&[data-placement^='left'] > .tippy-arrow::before {
border-left-color: $gray;
}
+
&[data-placement^='right'] > .tippy-arrow::before {
border-right-color: $gray;
}
}
-.tag {
- padding: 0.5rem;
- font-family: system-ui;
- font-size: 0.75rem;
- line-height: 1rem;
- display: inline-block;
- background-color: hsla(213, 27%, 84%, 1);
- border-radius: 0.375rem;
- text-transform: uppercase;
- color: hsla(215, 25%, 27%, 1);
- font-weight: 600;
- letter-spacing: 0.025em;
- margin-right: 0.75rem;
-}
-
.tippy-box[data-theme~='nx'] h4 {
font-family: monospace;
}
+
.tippy-box[data-theme~='nx'] p {
margin: 0.375rem;
}
+
.tippy-box[data-theme~='nx'] button {
background-color: rgba(249, 250, 251, 1);
border-color: $gray;
@@ -124,6 +113,7 @@ canvas {
border-color: rgb(71, 85, 105, 1);
color: rgb(203, 213, 225, 1);
}
+
button:hover {
background-color: rgb(51, 65, 85, 1);
}