UX: consistent share modal & popup, refactoring (#13759)

This commit is contained in:
Kris 2021-07-18 21:34:44 -04:00 committed by GitHub
parent 1c82989f77
commit 8de8989576
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 305 additions and 302 deletions

View File

@ -49,8 +49,11 @@ export default Component.extend({
if (this.element) { if (this.element) {
const linkInput = this.element.querySelector("#share-link input"); const linkInput = this.element.querySelector("#share-link input");
linkInput.value = this.link; linkInput.value = this.link;
linkInput.setSelectionRange(0, this.link.length); if (!this.site.mobileView) {
linkInput.focus(); // if the input is auto-focused on mobile, iOS requires two taps of the copy button
linkInput.setSelectionRange(0, this.link.length);
linkInput.focus();
}
} }
}, 200); }, 200);
}, },

View File

@ -4,13 +4,21 @@
{{#if date}} {{#if date}}
<span class="date">{{displayDate}}</span> <span class="date">{{displayDate}}</span>
{{/if}} {{/if}}
{{d-button
action=(action "close")
class="btn btn-flat close"
icon="times"
aria-label="share.close"
title="share.close"
}}
</div> </div>
<div> <div class="share-link-container">
<input type="text" aria-label={{i18n "share.url"}}> <input class="share-link-input" type="text" aria-label={{i18n "share.url"}}> {{copy-button selector="input.share-link-input"}}
</div> </div>
<div class="actions"> <div class="share-link-actions">
<div class="sources"> <div class="sources">
{{#each sources as |s|}} {{#each sources as |s|}}
{{share-source source=s title=model.title action=(action "share")}} {{share-source source=s title=model.title action=(action "share")}}
@ -19,27 +27,26 @@
<div class="alt-actions"> <div class="alt-actions">
{{#if topic.details.can_reply_as_new_topic}} {{#if topic.details.can_reply_as_new_topic}}
<div class="reply-as-new-topic"> {{#if topic.isPrivateMessage}}
{{#if topic.isPrivateMessage}} {{d-button
<a href class="new-topic" {{action "replyAsNewTopic"}} aria-label={{i18n "post.reply_as_new_private_message"}} title={{i18n "post.reply_as_new_private_message"}}> action=(action "replyAsNewTopic")
{{d-icon "plus"}} class="btn btn-default new-topic"
{{i18n "user.new_private_message"}} icon="plus"
</a> aria-label="post.reply_as_new_private_message"
{{else}} title="post.reply_as_new_private_message"
<a href class="new-topic" {{action "replyAsNewTopic"}} aria-label={{i18n "post.reply_as_new_topic"}} title={{i18n "post.reply_as_new_topic"}}> label="user.new_private_message"
{{d-icon "plus"}} }}
{{i18n "topic.create"}} {{else}}
</a> {{d-button
{{/if}} action=(action "replyAsNewTopic")
</div> class="btn btn-default new-topic"
icon="plus"
aria-label="post.reply_as_new_topic"
title="post.reply_as_new_topic"
label="topic.create"
}}
{{/if}}
{{/if}} {{/if}}
{{d-button
action=(action "close")
class="btn btn-flat close"
icon="times"
aria-label="share.close"
title="share.close"
}}
</div> </div>
</div> </div>

View File

@ -1 +1,7 @@
{{d-button action=(action "share" source) class=(concat "share-" source.id) translatedTitle=source.title translatedAriaLabel=source.title icon=(if source.icon source.icon source.htmlIcon)}} {{d-button
action=(action "share" source)
class=(concat "btn-default share-" source.id)
translatedTitle=source.title
translatedAriaLabel=source.title
icon=(if source.icon source.icon source.htmlIcon)
}}

View File

@ -2,7 +2,7 @@
<form> <form>
<div class="input-group invite-link"> <div class="input-group invite-link">
<label for="invite-link">{{i18n "user.invited.invite.instructions"}}</label> <label for="invite-link">{{i18n "user.invited.invite.instructions"}}</label>
<div class="invite-input-with-button"> <div class="share-link-container">
{{input {{input
name="invite-link" name="invite-link"
class="invite-link" class="invite-link"
@ -19,7 +19,7 @@
<div class="input-group input-email"> <div class="input-group input-email">
<label for="invite-email">{{d-icon "envelope"}}{{i18n "user.invited.invite.restrict_email"}}</label> <label for="invite-email">{{d-icon "envelope"}}{{i18n "user.invited.invite.restrict_email"}}</label>
<div class="invite-input-with-button"> <div class="invite-email-container">
{{input {{input
id="invite-email" id="invite-email"
value=buffered.email value=buffered.email

View File

@ -2,7 +2,7 @@
<form> <form>
<div class="input-group invite-link"> <div class="input-group invite-link">
<label for="invite-link">{{i18n "topic.share.instructions"}}</label> <label for="invite-link">{{i18n "topic.share.instructions"}}</label>
<div class="invite-input-with-button"> <div class="share-link-container">
{{input {{input
name="invite-link" name="invite-link"
class="invite-link" class="invite-link"
@ -12,50 +12,51 @@
{{copy-button selector="input.invite-link"}} {{copy-button selector="input.invite-link"}}
</div> </div>
</div> </div>
<div class="share-link-actions">
<div class="sources">
{{#each sources as |s|}}
{{share-source source=s title=topic.title action=(action "share")}}
{{/each}}
<div class="sources">
{{#each sources as |s|}}
{{share-source source=s title=topic.title action=(action "share")}}
{{/each}}
{{d-button
class="btn-default"
label="topic.share.notify_users.title"
icon="hand-point-right"
action=(action "toggleNotifyUsers")
}}
{{#if allowInvites}}
{{d-button {{d-button
class="btn-default" class="btn-default"
label="topic.share.invite_users" label="topic.share.notify_users.title"
icon="user-plus" icon="hand-point-right"
action=(action "inviteUsers") action=(action "toggleNotifyUsers")
}} }}
{{#if allowInvites}}
{{d-button
class="btn-default"
label="topic.share.invite_users"
icon="user-plus"
action=(action "inviteUsers")
}}
{{/if}}
</div>
{{#if showNotifyUsers}}
<div class="input-group invite-users">
<label for="invite-users">{{i18n "topic.share.notify_users.instructions"}}</label>
<div class="notify-user-input">
{{user-chooser
value=users
onChange=(action "onChangeUsers")
options=(hash
topicId=topic.id
maximum=(unless currentUser.staff 1)
excludeCurrentUser=true
)
}}
{{d-button
icon="check"
class="btn-primary"
disabled=(if users false true)
action=(action "notifyUsers")
}}
</div>
</div>
{{/if}} {{/if}}
</div> </div>
{{#if showNotifyUsers}}
<div class="input-group invite-users">
<label for="invite-users">{{i18n "topic.share.notify_users.instructions"}}</label>
<div class="invite-input-with-button">
{{user-chooser
value=users
onChange=(action "onChangeUsers")
options=(hash
topicId=topic.id
maximum=(unless currentUser.staff 1)
excludeCurrentUser=true
)
}}
{{d-button
icon="check"
class="btn-primary"
disabled=(if users false true)
action=(action "notifyUsers")
}}
</div>
</div>
{{/if}}
</form> </form>
{{/d-modal-body}} {{/d-modal-body}}

View File

@ -30,7 +30,7 @@ acceptance("Share and Invite modal", function (needs) {
"it shows the topic sharing url" "it shows the topic sharing url"
); );
assert.ok(count("button[class^='share-']") > 1, "it shows social sources"); assert.ok(count("button[class*='share-']") > 1, "it shows social sources");
assert.ok( assert.ok(
exists(".btn[aria-label='Notify']"), exists(".btn[aria-label='Notify']"),

View File

@ -40,7 +40,7 @@ acceptance("Topic", function (needs) {
test("Reply as new topic", async function (assert) { test("Reply as new topic", async function (assert) {
await visit("/t/internationalization-localization/280"); await visit("/t/internationalization-localization/280");
await click("button.share:nth-of-type(1)"); await click("button.share:nth-of-type(1)");
await click(".reply-as-new-topic a"); await click("button.new-topic");
assert.ok(exists(".d-editor-input"), "the composer input is visible"); assert.ok(exists(".d-editor-input"), "the composer input is visible");
@ -59,7 +59,7 @@ acceptance("Topic", function (needs) {
test("Reply as new message", async function (assert) { test("Reply as new message", async function (assert) {
await visit("/t/pm-for-testing/12"); await visit("/t/pm-for-testing/12");
await click("button.share:nth-of-type(1)"); await click("button.share:nth-of-type(1)");
await click(".reply-as-new-topic a"); await click("button.new-topic");
assert.ok(exists(".d-editor-input"), "the composer input is visible"); assert.ok(exists(".d-editor-input"), "the composer input is visible");

View File

@ -838,166 +838,6 @@
} }
} }
.create-invite-modal,
.share-topic-modal {
.input-group {
margin-bottom: 0.5em;
&:last-child {
margin-bottom: 0;
}
input[type="text"] {
width: 100%;
}
textarea#invite-message,
&.invite-to-topic input[type="text"],
.group-chooser,
.user-chooser,
.future-date-input-selector {
width: 100%;
}
&.invite-to-topic input[type="radio"] {
margin-left: 10px;
}
label .d-icon {
color: var(--primary-medium);
margin-right: 10px;
}
}
.input-group input[type="text"],
.input-group .btn,
.user-chooser .select-kit-header,
.future-date-input .select-kit-header {
height: 34px;
}
.invite-input-with-button {
display: flex;
.btn {
margin-left: 3px;
}
}
.input-group.input-expires-at,
.input-group.input-email,
.input-group.invite-max-redemptions {
margin-bottom: 0;
input[type="text"] {
width: unset;
}
}
.future-date-input {
.date-picker-wrapper {
input {
margin: 0;
}
}
.control-group:nth-child(2),
.control-group:nth-child(3) {
display: inline-block;
margin-bottom: 0;
width: 49%;
input {
margin-bottom: 0;
width: 150px;
}
}
}
.input-group.input-email {
align-items: baseline;
display: flex;
label {
display: inline;
}
.invite-input-with-button {
display: inline-flex;
flex: 1;
input[type="text"] {
flex: 1;
margin-left: 5px;
}
}
}
.invite-max-redemptions {
label {
display: inline;
}
input {
width: 80px;
}
}
.invite-to-topic {
#choose-topic-title {
margin-bottom: 0;
}
}
.show-advanced {
margin-left: auto;
margin-right: 0;
}
}
.create-invite-modal {
.input-group {
textarea#invite-message,
&.invite-to-topic input[type="text"],
.group-chooser,
.user-chooser,
.future-date-input-selector {
margin-left: 25px;
width: calc(100% - 25px);
}
}
}
.share-topic-modal {
form {
margin-bottom: 0;
}
.sources {
display: flex;
flex-wrap: wrap;
align-items: stretch;
button {
margin-right: 0.5em;
margin-bottom: 0.5em;
}
}
.share-twitter {
.d-icon {
color: var(--twitter);
}
}
.share-facebook {
.d-icon {
color: var(--facebook);
}
}
.invite-users {
margin: 1em 0;
}
}
.group-add-members-modal { .group-add-members-modal {
.input-group { .input-group {
margin-bottom: 0.5em; margin-bottom: 0.5em;

View File

@ -1,26 +1,80 @@
// styles that apply to the "share" popup when sharing a link to a post or topic // styles that apply to the "share" modal & popup when sharing a link to a post or topic
.share-link-container {
display: flex;
button {
transition-property: background-color, color; // don't transition outline
}
input {
width: 100%;
font-size: var(--font-up-1);
margin-bottom: 0;
&:focus + button {
outline: 1px solid var(--tertiary);
}
}
}
.share-link-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
button {
margin-top: 0.5em;
margin-right: 0.5em;
}
.sources {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.new-topic {
margin-right: 0;
}
.alt-actions {
display: flex;
justify-content: flex-end;
margin-left: auto;
}
}
.share-twitter {
.d-icon {
color: var(--twitter);
}
}
.share-facebook {
.d-icon {
color: var(--facebook);
}
}
// post share popup
#share-link { #share-link {
position: absolute; position: absolute;
left: 20px;
z-index: z("dropdown"); z-index: z("dropdown");
box-shadow: shadow("card"); box-shadow: shadow("card");
background-color: var(--secondary); background-color: var(--secondary);
padding: 8px 8px 4px 8px; padding: 0.5em;
width: 300px; width: 300px;
display: none; display: none;
&.visible { &.visible {
display: block; display: block;
} }
input[type="text"] { input[type="text"] {
width: 100%; width: 100%;
} }
.title { .title {
margin-bottom: 4px; margin-bottom: 0.5em;
align-items: center; align-items: center;
display: flex; display: flex;
justify-content: space-between;
h3 { h3 {
font-size: $font-0; font-size: $font-0;
@ -30,74 +84,166 @@
.date { .date {
font-weight: normal; font-weight: normal;
color: var(--primary-med-or-secondary-med); color: var(--primary-med-or-secondary-med);
} margin-left: 0.5em;
}
.copy-text {
display: inline-block;
position: absolute;
margin: 5px 5px 5px 15px;
color: var(--success);
opacity: 1;
transition: opacity 0.25s;
font-size: $font-0;
&:not(.success) {
opacity: 0;
}
}
.social-link {
margin-right: 8px;
font-size: $font-up-4;
.d-icon {
color: var(--tertiary-or-white);
}
.d-icon-fab-facebook {
// Adheres to Facebook brand guidelines
color: var(--facebook-or-white);
}
.d-icon-fab-twitter-square {
// Adheres to Twitter brand guidelines
color: var(--twitter-or-white);
}
}
input[type="text"] {
font-size: $font-up-1;
margin-bottom: 0;
}
.actions {
display: flex;
align-items: flex-end;
margin-top: 8px;
.sources {
display: flex;
align-items: center;
flex-wrap: wrap;
flex-direction: row;
max-width: 45%;
.social-link {
margin-right: 8px;
}
} }
.alt-actions { .btn.close {
display: flex; margin-left: auto;
align-items: center; }
flex: 1; }
justify-content: flex-end; }
height: 36px;
// topic share modal
.close-share {
font-size: $font-up-3; .share-topic-modal {
color: var(--primary-med-or-secondary-med); form {
} margin-bottom: 0;
}
.new-topic {
margin-right: 16px; .invite-users {
} display: flex;
flex-direction: column;
margin: 1.5em 0 0.25em;
width: 100%;
button {
margin-top: 0;
margin-right: 0;
}
}
}
.notify-user-input {
display: flex;
align-items: stretch;
.select-kit {
width: 100%;
}
.multi-select-header {
width: 100%;
}
.select-kit.multi-select .choices {
padding: 0;
}
.select-kit.multi-select.is-expanded + button {
outline: 1px solid var(--tertiary); // outline the button when the input is outlined, to match height
}
}
// topic invite modal
.create-invite-modal {
form {
margin: 0;
input[type="text"],
.btn,
.select-kit-header {
height: 2.27rem;
}
}
input {
margin-bottom: 0;
}
label {
margin-right: 0.5em;
.d-icon {
color: var(--primary-medium);
margin-right: 0.75em;
}
}
textarea {
margin-bottom: 0;
}
.input-group:not(:last-of-type) {
margin-bottom: 1em;
}
.input-group.input-expires-at,
.input-group.input-email,
.input-group.invite-max-redemptions {
input[type="text"] {
width: unset;
}
}
.existing-topic,
p {
// p is for "no topics found"
margin-left: 1.75em;
margin-top: 0.25em;
}
.future-date-input {
display: grid;
grid-template-columns: auto 1fr;
.control-group:nth-child(1) {
grid-column-start: 1;
grid-column-end: 3;
}
.control-group:nth-child(2) {
margin-left: 1.75em;
margin-right: 1.5em;
}
.control-group:nth-child(2),
.control-group:nth-child(3) {
grid-row-start: 2;
display: inline-flex;
align-items: center;
margin-bottom: 0;
input {
height: 100%;
margin-bottom: 0;
margin-left: 0.5em;
width: 150px;
}
}
}
.input-group.input-email {
display: flex;
align-items: baseline;
label {
display: inline;
}
}
.invite-email-container {
flex: 1 1 auto;
#invite-email {
width: 100%;
}
}
.invite-max-redemptions {
label {
display: inline;
}
input {
width: 80px;
}
}
.show-advanced {
margin-left: auto;
margin-right: 0;
}
.input-group {
textarea#invite-message,
&.invite-to-topic input[type="text"],
.group-chooser,
.user-chooser,
.future-date-input-selector {
margin-left: 1.75em;
width: calc(100% - 1.75em);
} }
} }
} }