From 58b0841360c742647243346c5faa183908e082ed Mon Sep 17 00:00:00 2001 From: Paul Mineev Date: Wed, 13 Apr 2022 19:08:47 -0700 Subject: [PATCH] update comment actions block --- frontend/app/common/types.ts | 7 - .../auth/components/button.module.css | 19 +- .../app/components/auth/components/button.tsx | 2 +- .../comment__action_type_collapse.css | 26 -- .../comment/__action/comment__action.css | 16 -- .../comment__control_select-label.css | 10 - .../_select/comment__control_select.css | 10 - .../comment__control_view_inactive.css | 7 - .../comment/__control/comment__control.css | 7 - .../comment/__controls/comment__controls.css | 16 -- .../__edit-timer/comment__edit-timer.css | 14 - .../comment/comment-actions.module.css | 32 +++ .../comment/comment-actions.spec.tsx | 143 ++++++++++ .../components/comment/comment-actions.tsx | 133 +++++++++ .../app/components/comment/comment.module.css | 24 +- .../app/components/comment/comment.test.tsx | 124 ++++---- frontend/app/components/comment/comment.tsx | 265 +++++------------- .../comment/getBlockingDurations.ts | 17 +- frontend/app/components/comment/styles.ts | 12 - frontend/app/components/countdown.tsx | 44 +++ frontend/app/components/countdown/index.tsx | 61 ---- frontend/app/components/profile/profile.tsx | 2 +- .../root__pinned-comments.css | 2 +- .../app/components/select/select.module.css | 6 +- frontend/app/components/select/select.tsx | 37 ++- frontend/app/components/sort-picker.spec.tsx | 10 +- frontend/app/locales/ar.json | 2 + frontend/app/locales/be.json | 2 + frontend/app/locales/bg.json | 2 + frontend/app/locales/bp.json | 2 + frontend/app/locales/de.json | 2 + frontend/app/locales/en.json | 2 + frontend/app/locales/es.json | 2 + frontend/app/locales/fi.json | 2 + frontend/app/locales/fr.json | 2 + frontend/app/locales/it.json | 2 + frontend/app/locales/ja.json | 2 + frontend/app/locales/ko.json | 2 + frontend/app/locales/pl.json | 2 + frontend/app/locales/ru.json | 2 + frontend/app/locales/tr.json | 2 + frontend/app/locales/ua.json | 2 + frontend/app/locales/vi.json | 2 + frontend/app/locales/zh.json | 2 + frontend/app/styles/global.css | 2 +- frontend/app/utils/exclude.ts | 8 - 46 files changed, 597 insertions(+), 495 deletions(-) delete mode 100644 frontend/app/components/comment/__action/_type/_collapse/comment__action_type_collapse.css delete mode 100644 frontend/app/components/comment/__action/comment__action.css delete mode 100644 frontend/app/components/comment/__control/_select-label/comment__control_select-label.css delete mode 100644 frontend/app/components/comment/__control/_select/comment__control_select.css delete mode 100644 frontend/app/components/comment/__control/_view/_inactive/comment__control_view_inactive.css delete mode 100644 frontend/app/components/comment/__control/comment__control.css delete mode 100644 frontend/app/components/comment/__controls/comment__controls.css delete mode 100644 frontend/app/components/comment/__edit-timer/comment__edit-timer.css create mode 100644 frontend/app/components/comment/comment-actions.module.css create mode 100644 frontend/app/components/comment/comment-actions.spec.tsx create mode 100644 frontend/app/components/comment/comment-actions.tsx create mode 100644 frontend/app/components/countdown.tsx delete mode 100644 frontend/app/components/countdown/index.tsx delete mode 100644 frontend/app/utils/exclude.ts diff --git a/frontend/app/common/types.ts b/frontend/app/common/types.ts index 7e64900d..1fe9177d 100644 --- a/frontend/app/common/types.ts +++ b/frontend/app/common/types.ts @@ -129,14 +129,7 @@ export interface Config { } export type Sorting = '-time' | '+time' | '-active' | '+active' | '-score' | '+score' | '-controversy' | '+controversy'; - export type BlockTTL = 'permanently' | '43200m' | '10080m' | '1440m'; - -export interface BlockingDuration { - label: string; - value: BlockTTL; -} - export type Theme = 'light' | 'dark'; /** diff --git a/frontend/app/components/auth/components/button.module.css b/frontend/app/components/auth/components/button.module.css index b660b5ec..94f6e199 100644 --- a/frontend/app/components/auth/components/button.module.css +++ b/frontend/app/components/auth/components/button.module.css @@ -76,7 +76,7 @@ } } -.link { +.hollow { color: rgb(var(--primary-color)); background-color: unset; text-transform: initial; @@ -87,6 +87,23 @@ } } +.link { + composes: hollow; + border: 0; + padding: 2px; + font-weight: 600; + + &:hover { + background-color: unset; + color: rgb(var(--primary-brighter-color)); + } +} + +.link[disabled] { + background-color: unset; + color: rgba(var(--primary-color), 0.9); +} + :global(.dark) { & .button { border-color: rgba(var(--white-color), 0.1); diff --git a/frontend/app/components/auth/components/button.tsx b/frontend/app/components/auth/components/button.tsx index b108319c..e3110d8a 100644 --- a/frontend/app/components/auth/components/button.tsx +++ b/frontend/app/components/auth/components/button.tsx @@ -5,7 +5,7 @@ import styles from './button.module.css'; type Props = Omit, 'size'> & { size?: 'xs' | 'sm'; - kind?: 'transparent' | 'link'; + kind?: 'transparent' | 'link' | 'hollow'; suffix?: VNode; loading?: boolean; selected?: boolean; diff --git a/frontend/app/components/comment/__action/_type/_collapse/comment__action_type_collapse.css b/frontend/app/components/comment/__action/_type/_collapse/comment__action_type_collapse.css deleted file mode 100644 index f35968a2..00000000 --- a/frontend/app/components/comment/__action/_type/_collapse/comment__action_type_collapse.css +++ /dev/null @@ -1,26 +0,0 @@ -.comment__action_type_collapse { - display: inline-block; - position: absolute; - top: 6px; - right: 68px; - box-sizing: border-box; - width: 12px; - height: 12px; - font-size: 12px; - line-height: 10px; - text-align: center; - border: 1px solid; - border-radius: 2px; - cursor: pointer; - - &:hover { - border-color: var(--color9); - color: var(--color9); - } - - &.comment__action_selected { - &:hover { - background: var(--color9); - } - } -} diff --git a/frontend/app/components/comment/__action/comment__action.css b/frontend/app/components/comment/__action/comment__action.css deleted file mode 100644 index 53d7e21c..00000000 --- a/frontend/app/components/comment/__action/comment__action.css +++ /dev/null @@ -1,16 +0,0 @@ -.comment__action { - font-size: 14px; - vertical-align: middle; - - & + .comment__action { - margin-left: 8px; - } - - & + .comment__controls { - &::before { - content: '•'; - margin-left: 8px; - margin-right: 8px; - } - } -} diff --git a/frontend/app/components/comment/__control/_select-label/comment__control_select-label.css b/frontend/app/components/comment/__control/_select-label/comment__control_select-label.css deleted file mode 100644 index 8248322d..00000000 --- a/frontend/app/components/comment/__control/_select-label/comment__control_select-label.css +++ /dev/null @@ -1,10 +0,0 @@ -.comment__control_select { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - opacity: 0; - width: 100%; - cursor: pointer; -} diff --git a/frontend/app/components/comment/__control/_select/comment__control_select.css b/frontend/app/components/comment/__control/_select/comment__control_select.css deleted file mode 100644 index 6fd7b0ec..00000000 --- a/frontend/app/components/comment/__control/_select/comment__control_select.css +++ /dev/null @@ -1,10 +0,0 @@ -.comment__control_select-label { - position: relative; - white-space: nowrap; - font-weight: 700; - - &::after { - content: '▾'; - margin-left: 2px; - } -} diff --git a/frontend/app/components/comment/__control/_view/_inactive/comment__control_view_inactive.css b/frontend/app/components/comment/__control/_view/_inactive/comment__control_view_inactive.css deleted file mode 100644 index a9087124..00000000 --- a/frontend/app/components/comment/__control/_view/_inactive/comment__control_view_inactive.css +++ /dev/null @@ -1,7 +0,0 @@ -.comment__control_view_inactive { - &, - &:hover, - &:focus { - pointer-events: none; - } -} diff --git a/frontend/app/components/comment/__control/comment__control.css b/frontend/app/components/comment/__control/comment__control.css deleted file mode 100644 index efbe0e32..00000000 --- a/frontend/app/components/comment/__control/comment__control.css +++ /dev/null @@ -1,7 +0,0 @@ -.comment__control { - margin-right: 8px; - - &:last-child { - margin-right: 0; - } -} diff --git a/frontend/app/components/comment/__controls/comment__controls.css b/frontend/app/components/comment/__controls/comment__controls.css deleted file mode 100644 index aaa1e48a..00000000 --- a/frontend/app/components/comment/__controls/comment__controls.css +++ /dev/null @@ -1,16 +0,0 @@ -.comment__controls { - display: inline; - vertical-align: middle; - user-select: none; - font-size: 14px; - - @media (hover: hover) { - opacity: 0; - transition: opacity 0.15s; - - &:hover, - &:focus-within { - opacity: 1; - } - } -} diff --git a/frontend/app/components/comment/__edit-timer/comment__edit-timer.css b/frontend/app/components/comment/__edit-timer/comment__edit-timer.css deleted file mode 100644 index 375d3cf6..00000000 --- a/frontend/app/components/comment/__edit-timer/comment__edit-timer.css +++ /dev/null @@ -1,14 +0,0 @@ -.comment__edit-timer { - font-size: 14px; - vertical-align: middle; - user-select: none; - margin-left: 8px; - - & + .comment__controls { - &::before { - content: '•'; - margin-left: 8px; - margin-right: 8px; - } - } -} diff --git a/frontend/app/components/comment/comment-actions.module.css b/frontend/app/components/comment/comment-actions.module.css new file mode 100644 index 00000000..e240207f --- /dev/null +++ b/frontend/app/components/comment/comment-actions.module.css @@ -0,0 +1,32 @@ +.root { + display: flex; + align-items: center; + font-size: 14px; + color: rgb(var(--primary-color)); +} + +.root > * + *, +.additionalActions > * + * { + margin-left: 0.5em; +} + +.countdown { + color: rgb(var(--secondary-darker-text-color)); +} + +@media (hover: hover) { + .additionalActions { + opacity: 0; + transition: opacity 0.15s; + } + + .root:hover .additionalActions { + opacity: 1; + } +} + +.additionalActions::before { + content: '•'; + color: rgb(var(--secondary-text-color)); + margin-right: 0.5em; +} diff --git a/frontend/app/components/comment/comment-actions.spec.tsx b/frontend/app/components/comment/comment-actions.spec.tsx new file mode 100644 index 00000000..e9dd25a0 --- /dev/null +++ b/frontend/app/components/comment/comment-actions.spec.tsx @@ -0,0 +1,143 @@ +import { h } from 'preact'; +import '@testing-library/jest-dom'; +import { CommentActions, Props } from './comment-actions'; +import { render } from 'tests/utils'; +import { screen, waitFor } from '@testing-library/preact'; + +function getProps(): Props { + return { + pinned: false, + admin: false, + currentUser: false, + copied: false, + bannedUser: false, + readOnly: false, + editing: false, + replying: false, + onCopy() {}, + onDelete() {}, + onTogglePin() {}, + onToggleReplying() {}, + onHideUser() {}, + onBlockUser() {}, + onUnblockUser() {}, + onDisableEditing() {}, + editable: false, + editDeadline: undefined, + }; +} +describe('', () => { + let props: Props; + + beforeEach(() => { + props = getProps(); + }); + + it('should render "Reply"', () => { + render(); + expect(screen.getByText('Reply')).toBeVisible(); + }); + + it('should not render "Reply" in read only mode', () => { + props.readOnly = true; + render(); + expect(screen.queryByText('Reply')).not.toBeInTheDocument(); + }); + + it('should not render "Cancel" instead "Reply" in replying mode', () => { + props.replying = true; + render(); + expect(screen.queryByText('Reply')).not.toBeInTheDocument(); + expect(screen.getByText('Cancel')).toBeInTheDocument(); + }); + + it('should render "Hide" on comments not from currentUser', () => { + props.currentUser = false; + render(); + expect(screen.getByText('Hide')).toBeVisible(); + }); + + it('should not render "Hide" on comments not from currentUser', () => { + props.currentUser = true; + render(); + expect(screen.queryByText('Hide')).not.toBeInTheDocument(); + }); + + it('should render "Edit" and timer when editing is available', async () => { + Object.assign(props, { editable: true, editDeadline: Date.now() + 300 * 1000 }); + render(); + expect(screen.getByText('Edit')).toBeInTheDocument(); + await waitFor(() => expect(['300s', '299s']).toContain(screen.getByRole('timer').textContent)); + }); + + it('should render "Cancel" instead "Edit" in editing mode', async () => { + Object.assign(props, { editable: true, editing: true, editDeadline: Date.now() + 300 * 1000 }); + render(); + expect(screen.getByText('Cancel')).toBeInTheDocument(); + }); + + it.each([ + [{ editable: false, editDeadline: Date.now() + 300 * 1000 }], + [{ editable: true, editDeadline: undefined }], + ] as Partial[][])('should not render "Edit" when editing is not available', (override) => { + Object.assign(props, override); + render(); + expect(screen.getByText('Hide')).toBeInTheDocument(); + }); + + it('should render "Delete" for current user comments', () => { + props.currentUser = true; + render(); + expect(screen.getByText('Delete')).toBeInTheDocument(); + }); + + it('should not render "Delete" for other users comments', () => { + render(); + expect(screen.queryByText('Delete')).not.toBeInTheDocument(); + }); + + describe('admin actions', () => { + it('should render "Copy"', () => { + props.admin = true; + render(); + expect(screen.getByText('Copy')).toBeInTheDocument(); + }); + + it('should render "Copied" when comment copied', () => { + Object.assign(props, { admin: true, copied: true }); + render(); + expect(screen.getByText('Copied!')).toBeInTheDocument(); + }); + + it('should render "Pin"', () => { + props.admin = true; + render(); + expect(screen.getByText('Pin')).toBeInTheDocument(); + }); + + it('should render "Unpin" when comment is pinned', () => { + Object.assign(props, { admin: true, pinned: true }); + render(); + expect(screen.getByText('Unpin')).toBeInTheDocument(); + }); + + it.each([[{ currentUser: false, admin: true }], [{ currentUser: true, admin: true }]] as Partial[][])( + 'should render "Delete" on all comments for admin', + (override) => { + Object.assign(props, override); + render(); + expect(screen.getByText('Delete')).toBeInTheDocument(); + } + ); + + it('should render admin actions in right order', () => { + props.admin = true; + render(); + expect(screen.getByTestId('comment-actions-additional').children[0]).toHaveTextContent('Hide'); + expect(screen.getByTestId('comment-actions-additional').children[1]).toHaveTextContent('Copy'); + expect(screen.getByTestId('comment-actions-additional').children[2]).toHaveTextContent('Pin'); + expect(screen.getByTestId('comment-actions-additional').children[3]).toHaveTextContent('Block'); + expect(screen.getByTestId('comment-actions-additional').children[4]).toHaveTextContent('Delete'); + }); + }); +}); diff --git a/frontend/app/components/comment/comment-actions.tsx b/frontend/app/components/comment/comment-actions.tsx new file mode 100644 index 00000000..a89337cb --- /dev/null +++ b/frontend/app/components/comment/comment-actions.tsx @@ -0,0 +1,133 @@ +import clsx from 'clsx'; +import { h, Fragment } from 'preact'; +import { defineMessages, useIntl } from 'react-intl'; + +import { BlockTTL } from 'common/types'; +import { Select } from 'components/select'; +import { Countdown } from 'components/countdown'; +import { Button } from 'components/auth/components/button'; + +import { getBlockingDurations } from './getBlockingDurations'; +import styles from './comment-actions.module.css'; + +export type Props = { + admin: boolean | undefined; + currentUser: boolean | undefined; + pinned: boolean | undefined; + copied: boolean | undefined; + bannedUser: boolean | undefined; + readOnly: boolean | undefined; + editing: boolean | undefined; + replying: boolean | undefined; + editable: boolean; + editDeadline: number | undefined; + onCopy(): void; + onDelete(): void; + onTogglePin(): void; + onToggleReplying(): void; + onHideUser(): void; + onBlockUser(ttl: BlockTTL): void; + onUnblockUser(): void; + onDisableEditing(): void; +}; + +export function CommentActions({ + admin, + pinned, + copied, + readOnly, + editable, + editing, + replying, + currentUser, + bannedUser, + editDeadline, + onCopy, + onDelete, + onTogglePin, + onToggleReplying, + onDisableEditing, + onHideUser, + onBlockUser, + onUnblockUser, +}: Props) { + const intl = useIntl(); + + const deleteJSX = ( + + ); + + return ( +
+ {!readOnly && ( + + )} + {editable && editDeadline && ( + <> + + + + + + )} +
+ {!currentUser && ( + + )} + {admin && ( + <> + + + {bannedUser ? ( + + ) : ( + - - {blockingDurations.map((block) => ( - - ))} - - - ); - } - - if (!this.props.data.delete) { - controls.push( - - ); - } - } - return controls; - }; - render(props: CommentProps, state: State): JSX.Element { const isAdmin = this.isAdmin(); const isGuest = this.isGuest(); @@ -394,9 +295,7 @@ export class Comment extends Component { const isReplying = props.editMode === CommentMode.Reply; const isEditing = props.editMode === CommentMode.Edit; - const editable = props.repliesCount === 0 && state.editDeadline; const uploadImageHandler = this.isAnonymous() ? undefined : this.props.uploadImage; - const commentControls = this.getCommentControls(); const intl = props.intl; const CommentForm = this.props.CommentForm || null; @@ -580,61 +479,27 @@ export class Comment extends Component { /> )} - {(!props.collapsed || props.view === 'pinned') && ( -
- {!props.data.delete && !props.isCommentsDisabled && !props.disabled && props.view === 'main' && ( - - )} - {!props.data.delete && - !props.disabled && - !!o.orig && - isCurrentUser && - (editable || isEditing) && - props.view === 'main' && [ - , - !isAdmin && ( - - ), - state.editDeadline && ( - - this.setState({ - editDeadline: null, - }) - } - /> - ), - ]} - - {commentControls.length > 0 && {commentControls}} -
+ {(!props.collapsed || !this.props.data.delete) && props.view !== 'pinned' && ( + this.setState({ editDeadline: undefined })} + currentUser={isCurrentUser} + bannedUser={props.isUserBanned} + onCopy={this.copyComment} + onTogglePin={this.togglePin} + onDelete={this.deleteComment} + onHideUser={this.hideUser} + onBlockUser={this.blockUser} + onUnblockUser={this.unblockUser} + /> )}
@@ -667,7 +532,7 @@ export class Comment extends Component { onSubmit={(text: string) => this.updateComment(props.data.id, text)} onCancel={this.toggleEditing} getPreview={this.props.getPreview!} - errorMessage={state.editDeadline === null ? intl.formatMessage(messages.expiredTime) : undefined} + errorMessage={state.editDeadline === undefined ? intl.formatMessage(messages.expiredTime) : undefined} autofocus={true} uploadImage={uploadImageHandler} simpleView={StaticStore.config.simple_view} diff --git a/frontend/app/components/comment/getBlockingDurations.ts b/frontend/app/components/comment/getBlockingDurations.ts index ec9c029c..f2d07b89 100644 --- a/frontend/app/components/comment/getBlockingDurations.ts +++ b/frontend/app/components/comment/getBlockingDurations.ts @@ -1,7 +1,16 @@ -import { BlockingDuration } from 'common/types'; -import { IntlShape, defineMessages } from 'react-intl'; +import { defineMessages, IntlShape } from 'react-intl'; +import { BlockTTL } from 'common/types'; + +export interface BlockingDuration { + label: string; + value: BlockTTL | undefined; +} const blockingMessages = defineMessages({ + block: { + id: 'comment.block', + defaultMessage: 'Block', + }, permanently: { id: 'blockingDuration.permanently', defaultMessage: 'Permanently', @@ -22,6 +31,10 @@ const blockingMessages = defineMessages({ export function getBlockingDurations(intl: IntlShape): BlockingDuration[] { return [ + { + label: intl.formatMessage(blockingMessages.block), + value: undefined, + }, { label: intl.formatMessage(blockingMessages.permanently), value: 'permanently', diff --git a/frontend/app/components/comment/styles.ts b/frontend/app/components/comment/styles.ts index 1eca82e1..cecd3f97 100644 --- a/frontend/app/components/comment/styles.ts +++ b/frontend/app/components/comment/styles.ts @@ -1,20 +1,8 @@ import 'components/raw-content'; import './comment.css'; - -import './__action/comment__action.css'; -import './__action/_type/_collapse/comment__action_type_collapse.css'; - -import './__edit-timer/comment__edit-timer.css'; - import './__body/comment__body.css'; -import './__control/comment__control.css'; -import './__control/_select/comment__control_select.css'; -import './__control/_select-label/comment__control_select-label.css'; -import './__control/_view/_inactive/comment__control_view_inactive.css'; - -import './__controls/comment__controls.css'; import './__info/comment__info.css'; import './__input/comment__input.css'; import './__link-to-parent/comment__link-to-parent.css'; diff --git a/frontend/app/components/countdown.tsx b/frontend/app/components/countdown.tsx new file mode 100644 index 00000000..f7edfa05 --- /dev/null +++ b/frontend/app/components/countdown.tsx @@ -0,0 +1,44 @@ +import { h, Fragment } from 'preact'; +import { useEffect, useRef, useState } from 'preact/hooks'; + +type Props = { + timestamp?: number; + onTimePassed?: () => void; +}; + +export function Countdown({ timestamp = 0, onTimePassed }: Props) { + const [value, setValue] = useState(calcRestTime(timestamp)); + const intervalIdRef = useRef(); + + useEffect(() => { + if (!timestamp) { + return; + } + + const intervalId = window.setInterval(() => setValue(calcRestTime(timestamp || 0)), 1000); + intervalIdRef.current = intervalId; + + setValue(calcRestTime(timestamp || 0)); + + return () => { + window.clearInterval(intervalId); + }; + }, [timestamp]); + + useEffect(() => { + if (value === 0) { + onTimePassed?.(); + window.clearInterval(intervalIdRef.current); + } + }, [value, onTimePassed]); + + if (!timestamp) { + return null; + } + + return {value}s; +} + +function calcRestTime(timestamp: number): number { + return Math.ceil(Math.max(0, (timestamp - Date.now()) / 1000)); +} diff --git a/frontend/app/components/countdown/index.tsx b/frontend/app/components/countdown/index.tsx deleted file mode 100644 index d6ebc387..00000000 --- a/frontend/app/components/countdown/index.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { h, JSX, Component, createRef } from 'preact'; -import { exclude } from 'utils/exclude'; - -type Props = { - time: Date; - onTimePassed?: () => void; -} & JSX.HTMLAttributes; - -interface State { - /** props.time converted to timestamp */ - time: number; -} - -/** Component which uses plain DOM mutation instead of rerendering react reactive reactivity */ -export class Countdown extends Component { - elemRef = createRef(); - intervalID?: number; - constructor(props: Props) { - super(props); - this.state = { - time: props.time.getTime(), - }; - } - componentDidMount() { - this.start(); - } - componentWillReceiveProps(nextProps: Props) { - if (nextProps.time === this.props.time) return; - this.setState({ - time: nextProps.time.getTime(), - }); - this.start(); - } - componentWillUnmount() { - window.clearInterval(this.intervalID); - } - shouldComponentUpdate() { - return false; - } - tick() { - if (this.elemRef) { - const value = Math.max(0, (this.state.time - new Date().getTime()) / 1000).toFixed(0); - this.elemRef.current!.innerText = value; - if (value === '0') { - this.props.onTimePassed && this.props.onTimePassed(); - window.clearInterval(this.intervalID); - this.intervalID = undefined; - } - } - } - start() { - if (this.intervalID) clearInterval(this.intervalID); - this.tick(); - this.intervalID = window.setInterval(() => { - this.tick(); - }, 1000); - } - render(props: Props) { - return ; - } -} diff --git a/frontend/app/components/profile/profile.tsx b/frontend/app/components/profile/profile.tsx index f39049d5..b1930a6a 100644 --- a/frontend/app/components/profile/profile.tsx +++ b/frontend/app/components/profile/profile.tsx @@ -215,7 +215,7 @@ export function Profile() { {isCurrent ? (
-
diff --git a/frontend/app/components/root/__pinned-comments/root__pinned-comments.css b/frontend/app/components/root/__pinned-comments/root__pinned-comments.css index f1b908b2..863ceb07 100644 --- a/frontend/app/components/root/__pinned-comments/root__pinned-comments.css +++ b/frontend/app/components/root/__pinned-comments/root__pinned-comments.css @@ -1,5 +1,5 @@ .root__pinned-comments { margin-top: 20px; - padding: 8px 12px; + padding: 0 12px 12px; border-radius: 2px; } diff --git a/frontend/app/components/select/select.module.css b/frontend/app/components/select/select.module.css index 46c59957..eac91574 100644 --- a/frontend/app/components/select/select.module.css +++ b/frontend/app/components/select/select.module.css @@ -4,7 +4,11 @@ align-items: center; padding: 2px; border-radius: 2px; - font-weight: bold; + font-weight: 600; +} + +.sm { + font-size: 14px; } .rootFocused { diff --git a/frontend/app/components/select/select.tsx b/frontend/app/components/select/select.tsx index 6ae2d174..3a33943c 100644 --- a/frontend/app/components/select/select.tsx +++ b/frontend/app/components/select/select.tsx @@ -8,29 +8,48 @@ import styles from './select.module.css'; type Item = { label: string | number; - value: string | number; + value: string | number | undefined; }; -type Props = { +type Props = Omit< + JSX.HTMLAttributes, + 'className' | 'onFocus' | 'onBlur' | 'selected' | 'label' | 'icon' | 'size' +> & { + size?: 'sm' | 'md'; items: Item[]; - selected: Item; -} & Omit, 'className' | 'onFocus' | 'onBlur' | 'selected'>; + selected?: Item; +}; -export function Select({ items, selected, ...props }: Props) { +export function Select({ items, selected, size = 'md', ...props }: Props) { const [focus, setFocus] = useState(false); + const selectedItem = selected ?? items[0]; + + const iconSize = { + sm: 10, + md: 12, + }; return ( - - {selected.label} - + + {selectedItem.label} +