= ({
// if the field is a part of react-hook-form form
inputOptions = { ...rest, ...methods.register(name, hookFormOptions) };
}
-
return (
{label && {label}}
@@ -181,8 +184,11 @@ const Input: React.FC = ({
type={type}
onKeyPress={keyPressEventHandler}
onPaste={pasteEventHandler}
+ ref={ref}
{...inputOptions}
/>
+ {clearIcon}
+
{withError && isHookFormField && (
@@ -192,6 +198,6 @@ const Input: React.FC = ({
);
-};
+});
export default Input;
diff --git a/kafka-ui-react-app/src/components/common/NewTable/ColoredCell.tsx b/kafka-ui-react-app/src/components/common/NewTable/ColoredCell.tsx
new file mode 100644
index 0000000000..df8ab2d6a8
--- /dev/null
+++ b/kafka-ui-react-app/src/components/common/NewTable/ColoredCell.tsx
@@ -0,0 +1,41 @@
+import React from 'react';
+import styled from 'styled-components';
+
+interface CellProps {
+ isWarning?: boolean;
+ isAttention?: boolean;
+}
+
+interface ColoredCellProps {
+ value: number | string;
+ warn?: boolean;
+ attention?: boolean;
+}
+
+const Cell = styled.div`
+ color: ${(props) => {
+ if (props.isAttention) {
+ return props.theme.table.colored.color.attention;
+ }
+
+ if (props.isWarning) {
+ return props.theme.table.colored.color.warning;
+ }
+
+ return 'inherit';
+ }};
+`;
+
+const ColoredCell: React.FC = ({
+ value,
+ warn,
+ attention,
+}) => {
+ return (
+
+ {value}
+ |
+ );
+};
+
+export default ColoredCell;
diff --git a/kafka-ui-react-app/src/components/common/NewTable/SizeCell.tsx b/kafka-ui-react-app/src/components/common/NewTable/SizeCell.tsx
index 00a60086d9..24485342aa 100644
--- a/kafka-ui-react-app/src/components/common/NewTable/SizeCell.tsx
+++ b/kafka-ui-react-app/src/components/common/NewTable/SizeCell.tsx
@@ -3,8 +3,15 @@ import { CellContext } from '@tanstack/react-table';
import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
-const SizeCell: React.FC> = ({ getValue }) => (
- ()} />
+type AsAny = any;
+
+const SizeCell: React.FC<
+ CellContext & { renderSegments?: boolean }
+> = ({ getValue, row, renderSegments = false }) => (
+ <>
+ ()} />
+ {renderSegments ? `, ${row?.original.count} segment(s)` : null}
+ >
);
export default SizeCell;
diff --git a/kafka-ui-react-app/src/components/common/Search/Search.tsx b/kafka-ui-react-app/src/components/common/Search/Search.tsx
index 66c0e95030..65116d645a 100644
--- a/kafka-ui-react-app/src/components/common/Search/Search.tsx
+++ b/kafka-ui-react-app/src/components/common/Search/Search.tsx
@@ -1,7 +1,9 @@
-import React from 'react';
+import React, { useRef } from 'react';
import { useDebouncedCallback } from 'use-debounce';
import Input from 'components/common/Input/Input';
import { useSearchParams } from 'react-router-dom';
+import CloseIcon from 'components/common/Icons/CloseIcon';
+import styled from 'styled-components';
interface SearchProps {
placeholder?: string;
@@ -10,6 +12,16 @@ interface SearchProps {
value?: string;
}
+const IconButtonWrapper = styled.span.attrs(() => ({
+ role: 'button',
+ tabIndex: '0',
+}))`
+ height: 16px !important;
+ display: inline-block;
+ &:hover {
+ cursor: pointer;
+ }
+`;
const Search: React.FC = ({
placeholder = 'Search',
disabled = false,
@@ -17,7 +29,11 @@ const Search: React.FC = ({
onChange,
}) => {
const [searchParams, setSearchParams] = useSearchParams();
+ const ref = useRef(null);
const handleChange = useDebouncedCallback((e) => {
+ if (ref.current != null) {
+ ref.current.value = e.target.value;
+ }
if (onChange) {
onChange(e.target.value);
} else {
@@ -28,6 +44,15 @@ const Search: React.FC = ({
setSearchParams(searchParams);
}
}, 500);
+ const clearSearchValue = () => {
+ if (searchParams.get('q')) {
+ searchParams.set('q', '');
+ setSearchParams(searchParams);
+ }
+ if (ref.current != null) {
+ ref.current.value = '';
+ }
+ };
return (
= ({
defaultValue={value || searchParams.get('q') || ''}
inputSize="M"
disabled={disabled}
+ ref={ref}
search
+ clearIcon={
+
+
+
+ }
/>
);
};
diff --git a/kafka-ui-react-app/src/components/common/Search/__tests__/Search.spec.tsx b/kafka-ui-react-app/src/components/common/Search/__tests__/Search.spec.tsx
index 808f229317..2103d22336 100644
--- a/kafka-ui-react-app/src/components/common/Search/__tests__/Search.spec.tsx
+++ b/kafka-ui-react-app/src/components/common/Search/__tests__/Search.spec.tsx
@@ -41,4 +41,24 @@ describe('Search', () => {
render();
expect(screen.queryByPlaceholderText('Search')).toBeInTheDocument();
});
+
+ it('Clear button is visible', () => {
+ render();
+
+ const clearButton = screen.getByRole('button');
+ expect(clearButton).toBeInTheDocument();
+ });
+
+ it('Clear button should clear text from input', async () => {
+ render();
+
+ const searchField = screen.getAllByRole('textbox')[0];
+ await userEvent.type(searchField, 'some text');
+ expect(searchField).toHaveValue('some text');
+
+ const clearButton = screen.getByRole('button');
+ await userEvent.click(clearButton);
+
+ expect(searchField).toHaveValue('');
+ });
});
diff --git a/kafka-ui-react-app/src/lib/__test__/yupExtended.spec.ts b/kafka-ui-react-app/src/lib/__test__/yupExtended.spec.ts
index 8100b9a326..bd43dd3f72 100644
--- a/kafka-ui-react-app/src/lib/__test__/yupExtended.spec.ts
+++ b/kafka-ui-react-app/src/lib/__test__/yupExtended.spec.ts
@@ -1,19 +1,5 @@
-import { isValidEnum, isValidJsonObject } from 'lib/yupExtended';
+import { isValidJsonObject } from 'lib/yupExtended';
-const invalidEnum = `
-ennum SchemType {
- AVRO = 0;
- JSON = 1;
- PROTOBUF = 3;
-}
-`;
-const validEnum = `
-enum SchemType {
- AVRO = 0;
- JSON = 1;
- PROTOBUF = 3;
-}
-`;
describe('yup extended', () => {
describe('isValidJsonObject', () => {
it('returns false for no value', () => {
@@ -35,21 +21,4 @@ describe('yup extended', () => {
expect(isValidJsonObject('{ "foo": "bar" }')).toBeTruthy();
});
});
-
- describe('isValidEnum', () => {
- it('returns false for invalid enum', () => {
- expect(isValidEnum(invalidEnum)).toBeFalsy();
- });
- it('returns false for no value', () => {
- expect(isValidEnum()).toBeFalsy();
- });
- it('returns true should trim value', () => {
- expect(
- isValidEnum(` enum SchemType {AVRO = 0; PROTOBUF = 3;} `)
- ).toBeTruthy();
- });
- it('returns true for valid enum', () => {
- expect(isValidEnum(validEnum)).toBeTruthy();
- });
- });
});
diff --git a/kafka-ui-react-app/src/lib/dateTimeHelpers.ts b/kafka-ui-react-app/src/lib/dateTimeHelpers.ts
index 3dce0edd78..148a70d2a3 100644
--- a/kafka-ui-react-app/src/lib/dateTimeHelpers.ts
+++ b/kafka-ui-react-app/src/lib/dateTimeHelpers.ts
@@ -1,6 +1,6 @@
export const formatTimestamp = (
timestamp?: number | string | Date,
- format: Intl.DateTimeFormatOptions = { hour12: false }
+ format: Intl.DateTimeFormatOptions = { hourCycle: 'h23' }
): string => {
if (!timestamp) {
return '';
@@ -8,7 +8,6 @@ export const formatTimestamp = (
// empty array gets the default one from the browser
const date = new Date(timestamp);
-
// invalid date
if (Number.isNaN(date.getTime())) {
return '';
diff --git a/kafka-ui-react-app/src/lib/yupExtended.ts b/kafka-ui-react-app/src/lib/yupExtended.ts
index 4c662ca822..241dac9770 100644
--- a/kafka-ui-react-app/src/lib/yupExtended.ts
+++ b/kafka-ui-react-app/src/lib/yupExtended.ts
@@ -10,7 +10,6 @@ declare module 'yup' {
TFlags extends yup.Flags = ''
> extends yup.Schema {
isJsonObject(message?: string): StringSchema;
- isEnum(message?: string): StringSchema;
}
}
@@ -40,32 +39,6 @@ const isJsonObject = (message?: string) => {
isValidJsonObject
);
};
-
-export const isValidEnum = (value?: string) => {
- try {
- if (!value) return false;
- const trimmedValue = value.trim();
- if (
- trimmedValue.indexOf('enum') === 0 &&
- trimmedValue.lastIndexOf('}') === trimmedValue.length - 1
- ) {
- return true;
- }
- } catch {
- // do nothing
- }
- return false;
-};
-
-const isEnum = (message?: string) => {
- return yup.string().test(
- 'isEnum',
- // eslint-disable-next-line no-template-curly-in-string
- message || '${path} is not Enum object',
- isValidEnum
- );
-};
-
/**
* due to yup rerunning all the object validiation during any render,
* it makes sense to cache the async results
@@ -88,7 +61,6 @@ export function cacheTest(
}
yup.addMethod(yup.StringSchema, 'isJsonObject', isJsonObject);
-yup.addMethod(yup.StringSchema, 'isEnum', isEnum);
export const topicFormValidationSchema = yup.object().shape({
name: yup
diff --git a/kafka-ui-react-app/src/theme/theme.ts b/kafka-ui-react-app/src/theme/theme.ts
index 09ff73510d..21a7746163 100644
--- a/kafka-ui-react-app/src/theme/theme.ts
+++ b/kafka-ui-react-app/src/theme/theme.ts
@@ -544,6 +544,12 @@ export const theme = {
active: Colors.neutral[90],
},
},
+ colored: {
+ color: {
+ attention: Colors.red[50],
+ warning: Colors.yellow[20],
+ },
+ },
expander: {
normal: Colors.brand[30],
hover: Colors.brand[40],
@@ -942,6 +948,12 @@ export const darkTheme: ThemeType = {
active: Colors.neutral[0],
},
},
+ colored: {
+ color: {
+ attention: Colors.red[50],
+ warning: Colors.yellow[20],
+ },
+ },
expander: {
normal: Colors.brand[30],
hover: Colors.brand[40],