diff --git a/.eslintrc.js b/.eslintrc.js index d59e0229a6d5d..7bf27ea373869 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -173,6 +173,10 @@ module.exports = { element: 'Collapse', message: 'use instead', }, + { + element: 'Checkbox', + message: 'use instead', + }, { element: 'MonacoEditor', message: 'use instead', diff --git a/frontend/__snapshots__/components-cards-text-card--template.png b/frontend/__snapshots__/components-cards-text-card--template.png index ef9d5e2dfdf91..4eee8836ab698 100644 Binary files a/frontend/__snapshots__/components-cards-text-card--template.png and b/frontend/__snapshots__/components-cards-text-card--template.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist.png b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist.png index e0e5eddc30ae1..b03f1fab581cf 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist.png and b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist.png differ diff --git a/frontend/src/lib/components/ResizableTable/VirtualTableHeader.tsx b/frontend/src/lib/components/ResizableTable/VirtualTableHeader.tsx deleted file mode 100644 index 3cb921ee1389d..0000000000000 --- a/frontend/src/lib/components/ResizableTable/VirtualTableHeader.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React, { useLayoutEffect, useState } from 'react' -import { InternalColumnType, ResizeHandler, ANTD_EXPAND_BUTTON_WIDTH } from './index' -import { ResizableBox } from 'react-resizable' -interface ResizableTitleProps { - children: React.ReactChild - onResize: ResizeHandler - initialWidth: number - height: number - minConstraints: [number, number] - maxConstraints: [number, number] -} - -interface VirtualTableHeaderProps { - columns: InternalColumnType[] - handleResize: (index: number) => ResizeHandler - layoutEffect?: CallableFunction - minColumnWidth: number - expandable?: Record -} - -function ResizableTitle({ - children, - onResize, - initialWidth, - height, - minConstraints, - maxConstraints, -}: ResizableTitleProps): JSX.Element { - const innerContent = ( -
-
{children}
-
- ) - const [width, setWidth] = useState(initialWidth) - const [isDragging, setIsDragging] = useState(false) - const handleResize: ResizeHandler = (event, data): void => { - setWidth(data.size.width) - onResize(event, data) - } - return ( -
- } - onResize={handleResize} - onResizeStart={(e) => { - e.preventDefault() - setIsDragging(true) - }} - onResizeStop={() => { - setIsDragging(false) - }} - draggableOpts={{ enableUserSelectHack: true }} - > - {innerContent} - -
- ) -} - -function VirtualTableHeader({ - columns, - handleResize, - layoutEffect, - minColumnWidth: defaultMinColumnWidth, - expandable, -}: VirtualTableHeaderProps): JSX.Element { - const height = 60 - useLayoutEffect(() => (typeof layoutEffect === 'function' ? layoutEffect() : undefined)) - return ( -
- {!!expandable && ( -
- )} - {columns.map(({ title, width, widthConstraints }, index) => { - const minColumnWidth = widthConstraints?.length ? widthConstraints[0] : defaultMinColumnWidth - const maxColumnWidth = widthConstraints?.length ? widthConstraints[1] : Infinity - return ( - - {title} - - ) - })} -
- ) -} - -export default React.memo(VirtualTableHeader) as typeof VirtualTableHeader diff --git a/frontend/src/lib/components/ResizableTable/index.scss b/frontend/src/lib/components/ResizableTable/index.scss deleted file mode 100644 index 5010fbe0cee52..0000000000000 --- a/frontend/src/lib/components/ResizableTable/index.scss +++ /dev/null @@ -1,81 +0,0 @@ -@import '../../../styles/mixins'; - -.resizable-table-scroll-container { - max-width: 100%; - overflow-x: auto; - position: relative; - - .table-gradient-overlay { - overflow-y: hidden; - - &.scrollable-right::after { - z-index: 99; - @extend %mixin-gradient-overlay-right; - right: 0; - width: 150px; - } - } - - .ant-table table { - border-collapse: collapse; - table-layout: fixed; - } -} - -.resizable-virtual-table-header { - display: flex; - - .react-resizable-wrapper { - background: rgb(250, 250, 250); - border-bottom: 1px solid #f0f0f0; - - &:last-child { - .resizable-handle { - display: none; - } - } - } - - .left-spacer { - background: rgb(250, 250, 250); - border-bottom: 1px solid #f0f0f0; - flex-grow: 0; - flex-shrink: 0; - } -} - -.react-resizable { - position: relative; - - .inner-wrapper { - height: 100%; - display: flex; - align-items: center; - } - .inner-text { - max-height: 8rem; - overflow: hidden; - padding: 8px; - font-weight: medium; - } - - .resizable-handle { - position: absolute; - right: 0; - top: 0; - z-index: 1; - width: 10px; - height: 100%; - cursor: col-resize; - border-right: 1px solid #f0f0f0; - transition: 0.2s border-color ease; - - &:hover { - border-color: var(--primary-light); - } - - &[data-drag-active='true'] { - border-color: var(--primary); - } - } -} diff --git a/frontend/src/lib/components/ResizableTable/index.tsx b/frontend/src/lib/components/ResizableTable/index.tsx deleted file mode 100644 index 1f560fd366752..0000000000000 --- a/frontend/src/lib/components/ResizableTable/index.tsx +++ /dev/null @@ -1,235 +0,0 @@ -import { useEffect, useLayoutEffect, useRef, useState } from 'react' -import { Table, TableProps } from 'antd' -import { ColumnType } from 'antd/lib/table' -import { ResizableProps } from 'react-resizable' -import ResizeObserver from 'resize-observer-polyfill' -import { RenderedCell } from 'rc-table/lib/interface' -import { getFullwidthColumnSize, getMinColumnWidth, parsePixelValue } from 'lib/utils/responsiveUtils' -import VirtualTableHeader from './VirtualTableHeader' -import { useBreakpoint } from 'lib/hooks/useBreakpoint' - -import './index.scss' - -export interface ResizableColumnType extends ColumnType { - title: string | JSX.Element - key?: string - dataIndex?: string - render?: - | ((record: RecordType, ...rest: any) => JSX.Element | string | RenderedCell | null) - | ((value: any, record?: RecordType, ...rest: any) => JSX.Element | string | RenderedCell | null) - ellipsis?: boolean - span: number - defaultWidth?: number - eventProperties?: string[] - widthConstraints?: [number, number] // Override default min and max width (px). To specify no max, use Infinity. -} - -export interface InternalColumnType extends ResizableColumnType { - width?: number -} - -export type ResizeHandler = Exclude - -// https://github.com/ant-design/ant-design/blob/4cdd24f4ec1ffb638175bb6c2dbb4fd7f103d60f/components/table/style/index.less#L422-L424 -export const ANTD_EXPAND_BUTTON_WIDTH = 48 - -interface ResizableTableProps extends TableProps { - columns: ResizableColumnType[] -} - -// Type matches antd.Table -export function ResizableTable = any>({ - columns: initialColumns = [], - components, - ...props -}: ResizableTableProps): JSX.Element { - const breakpoint = useBreakpoint() - const minColumnWidth = getMinColumnWidth(breakpoint) - const [columns, setColumns] = useState(() => { - const lastIndex = initialColumns.length - return initialColumns.map((col, index) => ({ - ...col, - width: index === lastIndex ? undefined : minColumnWidth, - })) as InternalColumnType[] - }) - const [headerColumns, setHeaderColumns] = useState(columns) - const [headerShouldRender, setHeaderShouldRender] = useState(false) - const scrollWrapperRef = useRef(null) - const overlayRef = useRef(null) - const timeout: any = useRef() - - function setScrollableRight(value: boolean): void { - if (value) { - return overlayRef?.current?.classList.add('scrollable-right') - } - return overlayRef?.current?.classList.remove('scrollable-right') - } - - function updateScrollGradient(): void { - if (overlayRef.current) { - const overlay = overlayRef.current - if (overlay.offsetWidth + overlay.scrollLeft < overlay.scrollWidth) { - setScrollableRight(true) - } else { - setScrollableRight(false) - } - } - } - - function getColumnCSSWidths(): Array { - const columnNodes = scrollWrapperRef.current?.querySelectorAll('.ant-table-content colgroup col') - if (columnNodes) { - const cols = Array.from(columnNodes) - return cols.map((col) => (col.style.width ? parsePixelValue(col.style.width) : undefined)) - } - return [] - } - - function updateColumnWidth(index: number, width: number): void { - const col = scrollWrapperRef.current?.querySelector( - // nth-child is 1-indexed. first column is fixed. last column width must be uncontrolled. - `.ant-table-content colgroup col:nth-child(${index + 1 + Number(!!props.expandable)}):not(:last-child)` - ) - col?.setAttribute('style', `width: ${width}px;`) - } - - function unsetLastColumnStyle(): void { - // last column width must be uncontrolled. - const col = scrollWrapperRef.current?.querySelector('.ant-table-content colgroup col:last-child') - col?.removeAttribute('style') - } - - function updateTableWidth(): void { - // elements have super strange auto-sizing: (https://css-tricks.com/fixing-tables-long-strings/) - // We control the width of the
based on the width of the virtual header. - const header = scrollWrapperRef.current?.querySelector('.resizable-virtual-table-header') - if (header?.childNodes) { - const children = Array.from(header?.childNodes) as HTMLElement[] - const headerWidth = children.reduce((total, { offsetWidth }) => total + (offsetWidth ?? 0), 0) - if (headerWidth) { - const table = scrollWrapperRef.current?.querySelector('.ant-table table') - table?.setAttribute('style', `width: ${headerWidth}px;`) - } - } - unsetLastColumnStyle() - } - - const handleColumnResize = - (index: number): ResizeHandler => - (_, { size: { width } }) => { - if (timeout.current) { - cancelAnimationFrame(timeout.current) - } - timeout.current = requestAnimationFrame(function () { - updateColumnWidth(index, width) - updateTableWidth() - }) - updateScrollGradient() - } - - function handleWrapperResize(newWidth: number): void { - // Recalculate column widths if the wrapper changes size. - const table = scrollWrapperRef.current?.querySelector('.ant-table table') - const oldWidth = table?.clientWidth - if (!oldWidth || oldWidth === newWidth) { - return - } - if (timeout.current) { - cancelAnimationFrame(timeout.current) - } - const resizeRatio = newWidth / oldWidth - const columnWidths = getColumnCSSWidths() - timeout.current = requestAnimationFrame(function () { - setHeaderShouldRender(false) - setHeaderColumns((cols) => { - const lastIndex = initialColumns.length - 1 - const nextColumns = cols.map((column, index) => - index === lastIndex - ? column - : { - ...column, - width: Math.max( - (columnWidths[index + Number(!!props.expandable)] ?? 0) * resizeRatio, - minColumnWidth - ), - } - ) - nextColumns.slice(0, lastIndex).forEach((col, index) => { - updateColumnWidth(index, col.width ?? minColumnWidth) - }) - updateTableWidth() - return nextColumns - }) - setHeaderShouldRender(true) - }) - } - - const resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => { - entries.forEach(({ contentRect: { width } }) => handleWrapperResize(width)) - }) - - useEffect(() => { - // Update render prop when parent columns change - setColumns((cols) => { - const lastIndex = cols.length - return cols.map((column, index) => - index === lastIndex - ? column - : { - ...column, - render: initialColumns[index].render, - } - ) - }) - }, [initialColumns]) - - useLayoutEffect(() => { - // Calculate relative column widths (px) once the wrapper is mounted. - if (scrollWrapperRef.current) { - resizeObserver.observe(scrollWrapperRef.current) - const wrapperWidth = scrollWrapperRef.current.clientWidth - const gridBasis = columns.reduce((total, { span }) => total + span, 0) - const columnSpanWidth = getFullwidthColumnSize(wrapperWidth, gridBasis) - setColumns((cols) => { - const lastIndex = cols.length - const nextColumns = cols.map((column, index) => - index === lastIndex - ? column - : { - ...column, - width: Math.max(column.defaultWidth || columnSpanWidth * column.span, minColumnWidth), - } - ) - setHeaderColumns(nextColumns) - return nextColumns - }) - updateScrollGradient() - setHeaderShouldRender(true) - } - }, []) - - return ( -
-
- {headerShouldRender && ( - - )} -
null }, // Nix that header row - }} - tableLayout="fixed" - {...props} - /> - - - ) -} diff --git a/frontend/src/lib/utils/responsiveUtils.tsx b/frontend/src/lib/utils/responsiveUtils.tsx index a96657793ca3d..0973a25df73ba 100644 --- a/frontend/src/lib/utils/responsiveUtils.tsx +++ b/frontend/src/lib/utils/responsiveUtils.tsx @@ -1,20 +1,10 @@ import { responsiveMap } from 'antd/lib/_util/responsiveObserve' -import { ANTD_EXPAND_BUTTON_WIDTH } from '../components/ResizableTable' const BREAKPOINT_MAP = Object.fromEntries( Object.entries(responsiveMap).map(([key, cssStatement]) => [key, parsePixelValue(cssStatement)]) ) const BREAKPOINT_VALUES = Object.values(BREAKPOINT_MAP).sort((a, b) => a - b) -export function getMinColumnWidth(breakpoint: number): number { - return breakpoint < 576 ? 150 : 50 -} - -export function getFullwidthColumnSize(wrapperWidth: number = 1200, gridBasis = 24): number { - const innerWidth = wrapperWidth - ANTD_EXPAND_BUTTON_WIDTH - return Math.floor(innerWidth / gridBasis) -} - export function parsePixelValue(cssStatement: string): number { return parseFloat(cssStatement.replace(/[^\d.]/g, '')) } diff --git a/frontend/src/scenes/batch_exports/BatchExportScene.tsx b/frontend/src/scenes/batch_exports/BatchExportScene.tsx index ba773f8f20d3a..b386c14819d6e 100644 --- a/frontend/src/scenes/batch_exports/BatchExportScene.tsx +++ b/frontend/src/scenes/batch_exports/BatchExportScene.tsx @@ -1,7 +1,14 @@ -import { Checkbox } from 'antd' import { SceneExport } from 'scenes/sceneTypes' import { PageHeader } from 'lib/components/PageHeader' -import { LemonButton, LemonDivider, LemonTable, LemonTag, LemonInput, LemonTableColumns } from '@posthog/lemon-ui' +import { + LemonButton, + LemonDivider, + LemonTable, + LemonTag, + LemonInput, + LemonTableColumns, + LemonCheckbox, +} from '@posthog/lemon-ui' import { urls } from 'scenes/urls' import { useActions, useValues } from 'kea' import { useEffect, useState } from 'react' @@ -324,14 +331,23 @@ export function LogsTab({ batchExportId }: BatchExportLogsProps): JSX.Element { onChange={setSearchTerm} allowClear /> -
+
Show logs of type:  - + {Object.values(BatchExportLogEntryLevel).map((type) => { + return ( + { + const newBatchExportLogsTypes = checked + ? [...batchExportLogsTypes, type] + : batchExportLogsTypes.filter((t) => t != type) + setBatchExportLogsTypes(newBatchExportLogsTypes) + }} + /> + ) + })}
-
- + { - setShowIdle(e.target.checked) - }} + onChange={setShowIdle} + label="Show idle queries" + /> + } + onClick={reloadQueries} > - Show idle queries - - + Reload Queries +
@@ -56,10 +59,15 @@ export function InternalMetricsTab(): JSX.Element { header: 'Clickhouse - currently running queries', content: ( <> -
- +
+ } + onClick={reloadQueries} + > + Reload Queries +
@@ -70,10 +78,15 @@ export function InternalMetricsTab(): JSX.Element { header: 'Clickhouse - slow query log (past 6 hours)', content: ( <> -
- +
+ } + onClick={reloadQueries} + > + Reload Queries +
diff --git a/frontend/src/scenes/plugins/Plugins.scss b/frontend/src/scenes/plugins/Plugins.scss index 75bcec7ae3fdf..afb04ca603640 100644 --- a/frontend/src/scenes/plugins/Plugins.scss +++ b/frontend/src/scenes/plugins/Plugins.scss @@ -1,7 +1,3 @@ -.Plugin__CapabilitiesTag { - cursor: default; -} - .Plugins__Popconfirm { z-index: var(--z-plugins-popconfirm); } diff --git a/frontend/src/scenes/plugins/edit/PluginDrawer.tsx b/frontend/src/scenes/plugins/edit/PluginDrawer.tsx index 1b8595953cfdd..e70bd9da2f19e 100644 --- a/frontend/src/scenes/plugins/edit/PluginDrawer.tsx +++ b/frontend/src/scenes/plugins/edit/PluginDrawer.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react' import { useActions, useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { Button, Form, Space, Switch, Tag } from 'antd' +import { Button, Form, Space, Switch } from 'antd' import { CodeOutlined, LockFilled } from '@ant-design/icons' import { userLogic } from 'scenes/userLogic' import { PluginImage } from 'scenes/plugins/plugin/PluginImage' @@ -18,7 +18,7 @@ import { PluginJobOptions } from './interface-jobs/PluginJobOptions' import { MOCK_NODE_PROCESS } from 'lib/constants' import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' import { PluginTags } from '../tabs/apps/components' -import { Link } from '@posthog/lemon-ui' +import { LemonTag, Link } from '@posthog/lemon-ui' window.process = MOCK_NODE_PROCESS @@ -209,12 +209,12 @@ export function PluginDrawer(): JSX.Element { ) .map((capability) => ( - {capability} + {capability} ))} {(editingPlugin.capabilities?.jobs || []).map((jobName) => ( - {jobName} + {jobName} ))}
diff --git a/frontend/src/scenes/plugins/plugin/PluginLogs.tsx b/frontend/src/scenes/plugins/plugin/PluginLogs.tsx index 445bc13974fb2..741ba55fdc2ea 100644 --- a/frontend/src/scenes/plugins/plugin/PluginLogs.tsx +++ b/frontend/src/scenes/plugins/plugin/PluginLogs.tsx @@ -1,11 +1,9 @@ -import { Checkbox } from 'antd' import { useActions, useValues } from 'kea' -import { ResizableColumnType, ResizableTable } from 'lib/components/ResizableTable' import { pluralize } from 'lib/utils' -import { PluginLogEntry, PluginLogEntryType } from '../../../types' +import { PluginLogEntryType } from '../../../types' import { LOGS_PORTION_LIMIT, pluginLogsLogic, PluginLogsProps } from './pluginLogsLogic' import { dayjs } from 'lib/dayjs' -import { LemonButton, LemonInput } from '@posthog/lemon-ui' +import { LemonButton, LemonCheckbox, LemonInput, LemonTable, LemonTableColumns } from '@posthog/lemon-ui' function PluginLogEntryTypeDisplay(type: PluginLogEntryType): JSX.Element { let color: string | undefined @@ -31,33 +29,29 @@ function PluginLogEntryTypeDisplay(type: PluginLogEntryType): JSX.Element { return {type} } -const columns: ResizableColumnType[] = [ +const columns: LemonTableColumns> = [ { title: 'Timestamp', key: 'timestamp', dataIndex: 'timestamp', - span: 3, render: (timestamp: string) => dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss.SSS UTC'), }, { title: 'Source', - key: 'source', dataIndex: 'source', - span: 1, - } as ResizableColumnType, + key: 'source', + }, { title: 'Type', key: 'type', dataIndex: 'type', - span: 1, render: PluginLogEntryTypeDisplay, - } as ResizableColumnType, + }, { title: 'Message', key: 'message', dataIndex: 'message', - span: 6, - } as ResizableColumnType, + }, ] export function PluginLogs({ pluginConfigId }: PluginLogsProps): JSX.Element { @@ -75,14 +69,23 @@ export function PluginLogs({ pluginConfigId }: PluginLogsProps): JSX.Element { onChange={setSearchTerm} allowClear /> -
+
Show logs of type:  - + {Object.values(PluginLogEntryType).map((type) => { + return ( + { + const newPluginLogsTypes = checked + ? [...pluginLogsTypes, type] + : pluginLogsTypes.filter((t) => t != type) + setPluginLogsTypes(newPluginLogsTypes) + }} + /> + ) + })}
- {!!pluginLogs.length && (