[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:
kamre 2021-09-25 07:56:21 +07:00 committed by GitHub
parent cc5c712bd0
commit 50470efe07
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 75 additions and 38 deletions

View file

@ -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"
/>

View file

@ -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;
}

View file

@ -35,6 +35,11 @@
right: 0;
}
.octo-tooltip {
display: flex;
max-width: 100%;
}
.octo-propertyvalue {
margin: 4px 0 0;
font-size: 12px;

View file

@ -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>
)

View file

@ -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 "

View file

@ -1,6 +1,4 @@
.DateRange {
width: 100%;
.inputContainer {
display: flex;

View file

@ -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)}
>

View file

@ -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>

View file

@ -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>
)

View file

@ -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>

View file

@ -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>
)

View file

@ -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"
/>

View file

@ -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;

View file

@ -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}

View file

@ -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"

View file

@ -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}

View file

@ -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)}

View file

@ -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>

View file

@ -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 {

View file

@ -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}