[GH-1057] Highlighting on hover for property values in the card dialog (#1065)
* Highlight property values on hover: - highlight on hover property values that are not readonly - make property name buttons the same width - set `min-width: 150px` for property values - add `readonly` class for LastModifiedAt/LastModifiedBy/CreatedAt * Make `Editable` used in card property values automatically expandable: - input width computation relies on `useLayoutEffect` and `getComputedStyle` - enable automatic expand for editable fields in `PropertyValueElement` - enable automatic expand for editable inside `URLProperty` - fix for tooltip display in `KanbanCard` * Fix issue with ellipsis in Chrome. * Support highlight on hover for `UserProperty` * Updating hover state and UI * Jest snapshot updated. * Fix jest snapshots * Update jest snapshot * Update jest snapshot Co-authored-by: Asaad Mahmood <asaadmahmood@users.noreply.github.com> Co-authored-by: Chen-I Lim <46905241+chenilim@users.noreply.github.com>
This commit is contained in:
parent
cc5c712bd0
commit
50470efe07
20 changed files with 75 additions and 38 deletions
|
@ -3,7 +3,7 @@
|
|||
exports[`components/propertyValueElement should match snapshot, date, array value 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="DateRange empty"
|
||||
class="DateRange empty octo-propertyvalue"
|
||||
>
|
||||
<button
|
||||
class="Button "
|
||||
|
@ -44,6 +44,7 @@ exports[`components/propertyValueElement should match snapshot, person, array va
|
|||
class="Editable octo-propertyvalue"
|
||||
placeholder="Empty"
|
||||
spellcheck="true"
|
||||
style="width: 5px;"
|
||||
title=""
|
||||
value=""
|
||||
/>
|
||||
|
@ -98,6 +99,7 @@ exports[`components/propertyValueElement should match snapshot, url, array value
|
|||
<input
|
||||
class="Editable octo-propertyvalue"
|
||||
placeholder="Empty"
|
||||
style="width: 5px;"
|
||||
title="http://localhost"
|
||||
value="http://localhost"
|
||||
/>
|
||||
|
@ -123,6 +125,7 @@ exports[`components/propertyValueElement should match snapshot, url, array value
|
|||
<input
|
||||
class="Editable octo-propertyvalue"
|
||||
placeholder="Empty"
|
||||
style="width: 5px;"
|
||||
title="http://localhost"
|
||||
value="http://localhost"
|
||||
/>
|
||||
|
|
|
@ -35,19 +35,33 @@
|
|||
|
||||
.octo-propertyrow {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 4px 0;
|
||||
height: 32px;
|
||||
|
||||
.octo-propertyvalue {
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
|
||||
&:not(.readonly) {
|
||||
min-width: 180px;
|
||||
transition: background 100ms ease-out 0s;
|
||||
cursor: pointer;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.08);
|
||||
}
|
||||
}
|
||||
|
||||
.MenuWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.Label {
|
||||
margin: 0 4px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.octo-propertyname {
|
||||
|
@ -56,6 +70,9 @@
|
|||
color: rgba(var(--center-channel-color-rgb), 0.6);
|
||||
|
||||
.Button {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
justify-content: unset;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,11 @@
|
|||
right: 0;
|
||||
}
|
||||
|
||||
.octo-tooltip {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.octo-propertyvalue {
|
||||
margin: 4px 0 0;
|
||||
font-size: 12px;
|
||||
|
|
|
@ -14,7 +14,7 @@ type Props = {
|
|||
const CreatedAt = (props: Props): JSX.Element => {
|
||||
const intl = useIntl()
|
||||
return (
|
||||
<div className='CreatedAt octo-propertyvalue'>
|
||||
<div className='CreatedAt octo-propertyvalue readonly'>
|
||||
{Utils.displayDateTime(new Date(props.createAt), intl)}
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exports[`components/properties/dateRange cancel set via text input 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="DateRange "
|
||||
class="DateRange octo-propertyvalue"
|
||||
>
|
||||
<button
|
||||
class="Button "
|
||||
|
@ -20,7 +20,7 @@ exports[`components/properties/dateRange cancel set via text input 1`] = `
|
|||
exports[`components/properties/dateRange handle clear 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="DateRange "
|
||||
class="DateRange octo-propertyvalue"
|
||||
>
|
||||
<button
|
||||
class="Button "
|
||||
|
@ -37,7 +37,7 @@ exports[`components/properties/dateRange handle clear 1`] = `
|
|||
exports[`components/properties/dateRange returns default correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="DateRange empty"
|
||||
class="DateRange empty octo-propertyvalue"
|
||||
>
|
||||
<button
|
||||
class="Button "
|
||||
|
@ -52,7 +52,7 @@ exports[`components/properties/dateRange returns default correctly 1`] = `
|
|||
exports[`components/properties/dateRange returns local correctly - es local 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="DateRange "
|
||||
class="DateRange octo-propertyvalue"
|
||||
>
|
||||
<button
|
||||
class="Button "
|
||||
|
@ -69,7 +69,7 @@ exports[`components/properties/dateRange returns local correctly - es local 1`]
|
|||
exports[`components/properties/dateRange set via text input 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="DateRange "
|
||||
class="DateRange octo-propertyvalue"
|
||||
>
|
||||
<button
|
||||
class="Button "
|
||||
|
@ -86,7 +86,7 @@ exports[`components/properties/dateRange set via text input 1`] = `
|
|||
exports[`components/properties/dateRange set via text input, es locale 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="DateRange "
|
||||
class="DateRange octo-propertyvalue"
|
||||
>
|
||||
<button
|
||||
class="Button "
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
.DateRange {
|
||||
width: 100%;
|
||||
|
||||
.inputContainer {
|
||||
display: flex;
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ function DateRange(props: Props): JSX.Element {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={`DateRange ${displayValue ? '' : 'empty'}`}>
|
||||
<div className={`DateRange ${displayValue ? '' : 'empty'} ` + className}>
|
||||
<Button
|
||||
onClick={() => setShowDialog(true)}
|
||||
>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exports[`componnets/properties/lastModifiedAt should match snapshot 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="LastModifiedAt octo-propertyvalue"
|
||||
class="LastModifiedAt octo-propertyvalue readonly"
|
||||
>
|
||||
June 15, 4:22 PM
|
||||
</div>
|
||||
|
|
|
@ -29,7 +29,7 @@ const LastModifiedAt = (props: Props): JSX.Element => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='LastModifiedAt octo-propertyvalue'>
|
||||
<div className='LastModifiedAt octo-propertyvalue readonly'>
|
||||
{Utils.displayDateTime(new Date(latestBlock.updateAt), intl)}
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exports[`components/properties/lastModifiedBy should match snapshot 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="LastModifiedBy octo-propertyvalue"
|
||||
class="LastModifiedBy octo-propertyvalue readonly"
|
||||
>
|
||||
username_1
|
||||
</div>
|
||||
|
|
|
@ -31,7 +31,7 @@ const LastModifiedBy = (props: Props): JSX.Element => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className='LastModifiedBy octo-propertyvalue'>
|
||||
<div className='LastModifiedBy octo-propertyvalue readonly'>
|
||||
{(workspaceUsersById && workspaceUsersById[latestBlock.modifiedBy]?.username) || latestBlock.modifiedBy}
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -7,6 +7,7 @@ exports[`components/properties/link returns link properties correctly 1`] = `
|
|||
>
|
||||
<input
|
||||
class="Editable octo-propertyvalue"
|
||||
style="width: 5px;"
|
||||
title="https://github.com/mattermost/focalboard"
|
||||
value="https://github.com/mattermost/focalboard"
|
||||
/>
|
||||
|
|
|
@ -4,11 +4,8 @@
|
|||
|
||||
&.url {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
padding: 0 8px; // increases clickable area for better UX
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.Link__button {
|
||||
|
@ -34,10 +31,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background: unset;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.Link__button {
|
||||
display: flex;
|
||||
|
|
|
@ -41,6 +41,7 @@ const URLProperty = (props: Props): JSX.Element => {
|
|||
className='octo-propertyvalue'
|
||||
placeholderText={props.placeholder}
|
||||
value={props.value}
|
||||
autoExpand={true}
|
||||
readonly={props.readonly}
|
||||
onChange={props.onChange}
|
||||
onSave={props.onSave}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exports[`components/properties/user not readonly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="UserProperty css-2b097c-container"
|
||||
class="UserProperty octo-propertyvalue css-2b097c-container"
|
||||
>
|
||||
<span
|
||||
aria-atomic="false"
|
||||
|
@ -96,7 +96,7 @@ exports[`components/properties/user not readonly 1`] = `
|
|||
exports[`components/properties/user not readonly not existing user 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="UserProperty css-2b097c-container"
|
||||
class="UserProperty octo-propertyvalue css-2b097c-container"
|
||||
>
|
||||
<span
|
||||
aria-atomic="false"
|
||||
|
@ -182,7 +182,7 @@ exports[`components/properties/user readonly view 1`] = `
|
|||
exports[`components/properties/user user dropdown open 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="UserProperty css-2b097c-container"
|
||||
class="UserProperty octo-propertyvalue css-2b097c-container"
|
||||
>
|
||||
<span
|
||||
aria-atomic="false"
|
||||
|
|
|
@ -40,7 +40,7 @@ const UserProperty = (props: Props): JSX.Element => {
|
|||
isSearchable={true}
|
||||
isClearable={true}
|
||||
backspaceRemovesValue={true}
|
||||
className={'UserProperty'}
|
||||
className={'UserProperty octo-propertyvalue'}
|
||||
styles={selectStyles}
|
||||
placeholder={'Empty'}
|
||||
getOptionLabel={(o: IUser) => o.username}
|
||||
|
|
|
@ -233,6 +233,7 @@ const PropertyValueElement = (props:Props): JSX.Element => {
|
|||
className='octo-propertyvalue'
|
||||
placeholderText={emptyDisplayValue}
|
||||
value={value.toString()}
|
||||
autoExpand={true}
|
||||
onChange={setValue}
|
||||
onSave={saveTextProperty}
|
||||
onCancel={() => setValue(propertyValue)}
|
||||
|
|
|
@ -217,7 +217,7 @@ exports[`components/table/Table extended should match snapshot with CreatedBy 1`
|
|||
style="width: 100px;"
|
||||
>
|
||||
<div
|
||||
class="CreatedAt octo-propertyvalue"
|
||||
class="CreatedAt octo-propertyvalue readonly"
|
||||
>
|
||||
June 15, 4:22 PM
|
||||
</div>
|
||||
|
@ -305,7 +305,7 @@ exports[`components/table/Table extended should match snapshot with CreatedBy 1`
|
|||
style="width: 100px;"
|
||||
>
|
||||
<div
|
||||
class="CreatedAt octo-propertyvalue"
|
||||
class="CreatedAt octo-propertyvalue readonly"
|
||||
>
|
||||
June 15, 4:22 PM
|
||||
</div>
|
||||
|
@ -993,7 +993,7 @@ exports[`components/table/Table extended should match snapshot with UpdatedAt 1`
|
|||
style="width: 100px;"
|
||||
>
|
||||
<div
|
||||
class="LastModifiedAt octo-propertyvalue"
|
||||
class="LastModifiedAt octo-propertyvalue readonly"
|
||||
>
|
||||
June 20, 12:22 PM
|
||||
</div>
|
||||
|
@ -1081,7 +1081,7 @@ exports[`components/table/Table extended should match snapshot with UpdatedAt 1`
|
|||
style="width: 100px;"
|
||||
>
|
||||
<div
|
||||
class="LastModifiedAt octo-propertyvalue"
|
||||
class="LastModifiedAt octo-propertyvalue readonly"
|
||||
>
|
||||
June 22, 11:23 AM
|
||||
</div>
|
||||
|
@ -1381,7 +1381,7 @@ exports[`components/table/Table extended should match snapshot with UpdatedBy 1`
|
|||
style="width: 100px;"
|
||||
>
|
||||
<div
|
||||
class="LastModifiedBy octo-propertyvalue"
|
||||
class="LastModifiedBy octo-propertyvalue readonly"
|
||||
>
|
||||
username_4
|
||||
</div>
|
||||
|
@ -1469,7 +1469,7 @@ exports[`components/table/Table extended should match snapshot with UpdatedBy 1`
|
|||
style="width: 100px;"
|
||||
>
|
||||
<div
|
||||
class="LastModifiedBy octo-propertyvalue"
|
||||
class="LastModifiedBy octo-propertyvalue readonly"
|
||||
>
|
||||
username_3
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
padding: 0 10px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.1);
|
||||
background-color: rgba(var(--center-channel-color-rgb), 0.08);
|
||||
}
|
||||
|
||||
&.filled {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {forwardRef, useImperativeHandle, useRef} from 'react'
|
||||
import React, {forwardRef, useImperativeHandle, useLayoutEffect, useRef} from 'react'
|
||||
|
||||
import './editable.scss'
|
||||
|
||||
|
@ -12,6 +12,7 @@ export type EditableProps = {
|
|||
saveOnEsc?: boolean
|
||||
readonly?: boolean
|
||||
spellCheck?: boolean
|
||||
autoExpand?: boolean
|
||||
|
||||
validator?: (value: string) => boolean
|
||||
onCancel?: () => void
|
||||
|
@ -115,9 +116,26 @@ export function useEditable(
|
|||
}
|
||||
}
|
||||
|
||||
function borderWidth(style: CSSStyleDeclaration): number {
|
||||
return (
|
||||
parseInt(style.borderLeftWidth || '0', 10) +
|
||||
parseInt(style.borderRightWidth || '0', 10)
|
||||
)
|
||||
}
|
||||
|
||||
const Editable = (props: EditableProps, ref: React.Ref<Focusable>): JSX.Element => {
|
||||
const elementRef = useRef<HTMLInputElement>(null)
|
||||
const elementProps = useEditable(props, ref, elementRef)
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (props.autoExpand && elementRef.current) {
|
||||
const input = elementRef.current
|
||||
const computed = getComputedStyle(input)
|
||||
input.style.width = 'auto'
|
||||
input.style.width = `${input.scrollWidth + borderWidth(computed) + 1}px`
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<input
|
||||
{...elementProps}
|
||||
|
|
Loading…
Reference in a new issue